nfp: add a mutex lock for the vNIC ctrl BAR
authorJakub Kicinski <jakub.kicinski@netronome.com>
Fri, 12 Apr 2019 03:27:05 +0000 (20:27 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 13 Apr 2019 00:29:15 +0000 (17:29 -0700)
Soon we will try to write to the vNIC mailbox without RTNL held.
Add a new mutex to protect access to specific parts of the PCI
control BAR.

Move the mailbox size checking to the mailbox lock() helper, where
it can be more effective (happen prior to potential overwrite of
other data).

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/abm/ctrl.c
drivers/net/ethernet/netronome/nfp/nfp_net.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h

index 9584f03..69e84ff 100644 (file)
@@ -261,10 +261,15 @@ int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm)
 
 int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed)
 {
+       const u32 cmd = NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET;
        struct nfp_net *nn = alink->vnic;
        unsigned int i;
        int err;
 
+       err = nfp_net_mbox_lock(nn, alink->abm->prio_map_len);
+       if (err)
+               return err;
+
        /* Write data_len and wipe reserved */
        nn_writeq(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATALEN,
                  alink->abm->prio_map_len);
@@ -273,8 +278,7 @@ int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed)
                nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATA + i,
                          packed[i / sizeof(u32)]);
 
-       err = nfp_net_reconfig_mbox(nn,
-                                   NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET);
+       err = nfp_net_mbox_reconfig_and_unlock(nn, cmd);
        if (err)
                nfp_err(alink->abm->app->cpp,
                        "setting DSCP -> VQ map failed with error %d\n", err);
index be37c2d..df9aff2 100644 (file)
@@ -539,12 +539,17 @@ struct nfp_net_dp {
  * @shared_handler:     Handler for shared interrupts
  * @shared_name:        Name for shared interrupt
  * @me_freq_mhz:        ME clock_freq (MHz)
- * @reconfig_lock:     Protects HW reconfiguration request regs/machinery
+ * @reconfig_lock:     Protects @reconfig_posted, @reconfig_timer_active,
+ *                     @reconfig_sync_present and HW reconfiguration request
+ *                     regs/machinery from async requests (sync must take
+ *                     @bar_lock)
  * @reconfig_posted:   Pending reconfig bits coming from async sources
  * @reconfig_timer_active:  Timer for reading reconfiguration results is pending
  * @reconfig_sync_present:  Some thread is performing synchronous reconfig
  * @reconfig_timer:    Timer for async reading of reconfig results
  * @reconfig_in_progress_update:       Update FW is processing now (debug only)
+ * @bar_lock:          vNIC config BAR access lock, protects: update,
+ *                     mailbox area
  * @link_up:            Is the link up?
  * @link_status_lock:  Protects @link_* and ensures atomicity with BAR reading
  * @rx_coalesce_usecs:      RX interrupt moderation usecs delay parameter
@@ -615,6 +620,8 @@ struct nfp_net {
        struct timer_list reconfig_timer;
        u32 reconfig_in_progress_update;
 
+       struct mutex bar_lock;
+
        u32 rx_coalesce_usecs;
        u32 rx_coalesce_max_frames;
        u32 tx_coalesce_usecs;
@@ -839,6 +846,16 @@ static inline void nfp_ctrl_unlock(struct nfp_net *nn)
        spin_unlock_bh(&nn->r_vecs[0].lock);
 }
 
+static inline void nn_ctrl_bar_lock(struct nfp_net *nn)
+{
+       mutex_lock(&nn->bar_lock);
+}
+
+static inline void nn_ctrl_bar_unlock(struct nfp_net *nn)
+{
+       mutex_unlock(&nn->bar_lock);
+}
+
 /* Globals */
 extern const char nfp_driver_version[];
 
@@ -871,7 +888,9 @@ unsigned int nfp_net_rss_key_sz(struct nfp_net *nn);
 void nfp_net_rss_write_itbl(struct nfp_net *nn);
 void nfp_net_rss_write_key(struct nfp_net *nn);
 void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
-int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd);
+int nfp_net_mbox_lock(struct nfp_net *nn, unsigned int data_size);
+int nfp_net_mbox_reconfig(struct nfp_net *nn, u32 mbox_cmd);
+int nfp_net_mbox_reconfig_and_unlock(struct nfp_net *nn, u32 mbox_cmd);
 
 unsigned int
 nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries,
index 60f2da4..ab84a6c 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
+#include <linux/lockdep.h>
 #include <linux/mm.h>
 #include <linux/overflow.h>
 #include <linux/page_ref.h>
@@ -260,7 +261,7 @@ static void nfp_net_reconfig_wait_posted(struct nfp_net *nn)
 }
 
 /**
- * nfp_net_reconfig() - Reconfigure the firmware
+ * __nfp_net_reconfig() - Reconfigure the firmware
  * @nn:      NFP Net device to reconfigure
  * @update:  The value for the update field in the BAR config
  *
@@ -270,10 +271,12 @@ static void nfp_net_reconfig_wait_posted(struct nfp_net *nn)
  *
  * Return: Negative errno on error, 0 on success
  */
-int nfp_net_reconfig(struct nfp_net *nn, u32 update)
+static int __nfp_net_reconfig(struct nfp_net *nn, u32 update)
 {
        int ret;
 
+       lockdep_assert_held(&nn->bar_lock);
+
        nfp_net_reconfig_sync_enter(nn);
 
        nfp_net_reconfig_start(nn, update);
@@ -291,8 +294,31 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)
        return ret;
 }
 
+int nfp_net_reconfig(struct nfp_net *nn, u32 update)
+{
+       int ret;
+
+       nn_ctrl_bar_lock(nn);
+       ret = __nfp_net_reconfig(nn, update);
+       nn_ctrl_bar_unlock(nn);
+
+       return ret;
+}
+
+int nfp_net_mbox_lock(struct nfp_net *nn, unsigned int data_size)
+{
+       if (nn->tlv_caps.mbox_len < NFP_NET_CFG_MBOX_SIMPLE_VAL + data_size) {
+               nn_err(nn, "mailbox too small for %u of data (%u)\n",
+                      data_size, nn->tlv_caps.mbox_len);
+               return -EIO;
+       }
+
+       nn_ctrl_bar_lock(nn);
+       return 0;
+}
+
 /**
- * nfp_net_reconfig_mbox() - Reconfigure the firmware via the mailbox
+ * nfp_net_mbox_reconfig() - Reconfigure the firmware via the mailbox
  * @nn:        NFP Net device to reconfigure
  * @mbox_cmd:  The value for the mailbox command
  *
@@ -300,19 +326,15 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)
  *
  * Return: Negative errno on error, 0 on success
  */
-int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)
+int nfp_net_mbox_reconfig(struct nfp_net *nn, u32 mbox_cmd)
 {
        u32 mbox = nn->tlv_caps.mbox_off;
        int ret;
 
-       if (!nfp_net_has_mbox(&nn->tlv_caps)) {
-               nn_err(nn, "no mailbox present, command: %u\n", mbox_cmd);
-               return -EIO;
-       }
-
+       lockdep_assert_held(&nn->bar_lock);
        nn_writeq(nn, mbox + NFP_NET_CFG_MBOX_SIMPLE_CMD, mbox_cmd);
 
-       ret = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_MBOX);
+       ret = __nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_MBOX);
        if (ret) {
                nn_err(nn, "Mailbox update error\n");
                return ret;
@@ -321,6 +343,15 @@ int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)
        return -nn_readl(nn, mbox + NFP_NET_CFG_MBOX_SIMPLE_RET);
 }
 
+int nfp_net_mbox_reconfig_and_unlock(struct nfp_net *nn, u32 mbox_cmd)
+{
+       int ret;
+
+       ret = nfp_net_mbox_reconfig(nn, mbox_cmd);
+       nn_ctrl_bar_unlock(nn);
+       return ret;
+}
+
 /* Interrupt configuration and handling
  */
 
@@ -3128,7 +3159,9 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
 static int
 nfp_net_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
+       const u32 cmd = NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD;
        struct nfp_net *nn = netdev_priv(netdev);
+       int err;
 
        /* Priority tagged packets with vlan id 0 are processed by the
         * NFP as untagged packets
@@ -3136,17 +3169,23 @@ nfp_net_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
        if (!vid)
                return 0;
 
+       err = nfp_net_mbox_lock(nn, NFP_NET_CFG_VLAN_FILTER_SZ);
+       if (err)
+               return err;
+
        nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_VID, vid);
        nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO,
                  ETH_P_8021Q);
 
-       return nfp_net_reconfig_mbox(nn, NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD);
+       return nfp_net_mbox_reconfig_and_unlock(nn, cmd);
 }
 
 static int
 nfp_net_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
 {
+       const u32 cmd = NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL;
        struct nfp_net *nn = netdev_priv(netdev);
+       int err;
 
        /* Priority tagged packets with vlan id 0 are processed by the
         * NFP as untagged packets
@@ -3154,11 +3193,15 @@ nfp_net_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
        if (!vid)
                return 0;
 
+       err = nfp_net_mbox_lock(nn, NFP_NET_CFG_VLAN_FILTER_SZ);
+       if (err)
+               return err;
+
        nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_VID, vid);
        nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO,
                  ETH_P_8021Q);
 
-       return nfp_net_reconfig_mbox(nn, NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL);
+       return nfp_net_mbox_reconfig_and_unlock(nn, cmd);
 }
 
 static void nfp_net_stat64(struct net_device *netdev,
@@ -3650,6 +3693,8 @@ nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev,
        nn->dp.txd_cnt = NFP_NET_TX_DESCS_DEFAULT;
        nn->dp.rxd_cnt = NFP_NET_RX_DESCS_DEFAULT;
 
+       mutex_init(&nn->bar_lock);
+
        spin_lock_init(&nn->reconfig_lock);
        spin_lock_init(&nn->link_status_lock);
 
@@ -3677,6 +3722,9 @@ err_free_nn:
 void nfp_net_free(struct nfp_net *nn)
 {
        WARN_ON(timer_pending(&nn->reconfig_timer) || nn->reconfig_posted);
+
+       mutex_destroy(&nn->bar_lock);
+
        if (nn->dp.netdev)
                free_netdev(nn->dp.netdev);
        else
index f5d564b..25919e3 100644 (file)
 #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_SIMPLE_LEN    12
 
 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1
 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2
@@ -495,10 +494,4 @@ struct nfp_net_tlv_caps {
 
 int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                           struct nfp_net_tlv_caps *caps);
-
-static inline bool nfp_net_has_mbox(struct nfp_net_tlv_caps *caps)
-{
-       return caps->mbox_len >= NFP_NET_CFG_MBOX_SIMPLE_LEN;
-}
-
 #endif /* _NFP_NET_CTRL_H_ */