nfp: add a mutex lock for the vNIC ctrl BAR

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>
This commit is contained in:
Jakub Kicinski 2019-04-11 20:27:05 -07:00 committed by David S. Miller
parent e64718282c
commit dd5b2498d8
4 changed files with 87 additions and 23 deletions

View 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) 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; struct nfp_net *nn = alink->vnic;
unsigned int i; unsigned int i;
int err; int err;
err = nfp_net_mbox_lock(nn, alink->abm->prio_map_len);
if (err)
return err;
/* Write data_len and wipe reserved */ /* Write data_len and wipe reserved */
nn_writeq(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATALEN, nn_writeq(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATALEN,
alink->abm->prio_map_len); 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, nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATA + i,
packed[i / sizeof(u32)]); packed[i / sizeof(u32)]);
err = nfp_net_reconfig_mbox(nn, err = nfp_net_mbox_reconfig_and_unlock(nn, cmd);
NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET);
if (err) if (err)
nfp_err(alink->abm->app->cpp, nfp_err(alink->abm->app->cpp,
"setting DSCP -> VQ map failed with error %d\n", err); "setting DSCP -> VQ map failed with error %d\n", err);

View File

@ -539,12 +539,17 @@ struct nfp_net_dp {
* @shared_handler: Handler for shared interrupts * @shared_handler: Handler for shared interrupts
* @shared_name: Name for shared interrupt * @shared_name: Name for shared interrupt
* @me_freq_mhz: ME clock_freq (MHz) * @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_posted: Pending reconfig bits coming from async sources
* @reconfig_timer_active: Timer for reading reconfiguration results is pending * @reconfig_timer_active: Timer for reading reconfiguration results is pending
* @reconfig_sync_present: Some thread is performing synchronous reconfig * @reconfig_sync_present: Some thread is performing synchronous reconfig
* @reconfig_timer: Timer for async reading of reconfig results * @reconfig_timer: Timer for async reading of reconfig results
* @reconfig_in_progress_update: Update FW is processing now (debug only) * @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_up: Is the link up?
* @link_status_lock: Protects @link_* and ensures atomicity with BAR reading * @link_status_lock: Protects @link_* and ensures atomicity with BAR reading
* @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter * @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter
@ -615,6 +620,8 @@ struct nfp_net {
struct timer_list reconfig_timer; struct timer_list reconfig_timer;
u32 reconfig_in_progress_update; u32 reconfig_in_progress_update;
struct mutex bar_lock;
u32 rx_coalesce_usecs; u32 rx_coalesce_usecs;
u32 rx_coalesce_max_frames; u32 rx_coalesce_max_frames;
u32 tx_coalesce_usecs; 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); 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 */ /* Globals */
extern const char nfp_driver_version[]; 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_itbl(struct nfp_net *nn);
void nfp_net_rss_write_key(struct nfp_net *nn); void nfp_net_rss_write_key(struct nfp_net *nn);
void nfp_net_coalesce_write_cfg(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 unsigned int
nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries, nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries,

View File

@ -23,6 +23,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/lockdep.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/overflow.h> #include <linux/overflow.h>
#include <linux/page_ref.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 * @nn: NFP Net device to reconfigure
* @update: The value for the update field in the BAR config * @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 * 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; int ret;
lockdep_assert_held(&nn->bar_lock);
nfp_net_reconfig_sync_enter(nn); nfp_net_reconfig_sync_enter(nn);
nfp_net_reconfig_start(nn, update); nfp_net_reconfig_start(nn, update);
@ -291,8 +294,31 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)
return ret; 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 * @nn: NFP Net device to reconfigure
* @mbox_cmd: The value for the mailbox command * @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 * 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; u32 mbox = nn->tlv_caps.mbox_off;
int ret; int ret;
if (!nfp_net_has_mbox(&nn->tlv_caps)) { lockdep_assert_held(&nn->bar_lock);
nn_err(nn, "no mailbox present, command: %u\n", mbox_cmd);
return -EIO;
}
nn_writeq(nn, mbox + NFP_NET_CFG_MBOX_SIMPLE_CMD, mbox_cmd); 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) { if (ret) {
nn_err(nn, "Mailbox update error\n"); nn_err(nn, "Mailbox update error\n");
return ret; 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); 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 /* Interrupt configuration and handling
*/ */
@ -3128,7 +3159,9 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
static int static int
nfp_net_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) 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); struct nfp_net *nn = netdev_priv(netdev);
int err;
/* Priority tagged packets with vlan id 0 are processed by the /* Priority tagged packets with vlan id 0 are processed by the
* NFP as untagged packets * NFP as untagged packets
@ -3136,17 +3169,23 @@ nfp_net_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
if (!vid) if (!vid)
return 0; 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_VID, vid);
nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO, nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO,
ETH_P_8021Q); 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 static int
nfp_net_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) 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); struct nfp_net *nn = netdev_priv(netdev);
int err;
/* Priority tagged packets with vlan id 0 are processed by the /* Priority tagged packets with vlan id 0 are processed by the
* NFP as untagged packets * NFP as untagged packets
@ -3154,11 +3193,15 @@ nfp_net_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
if (!vid) if (!vid)
return 0; 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_VID, vid);
nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO, nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO,
ETH_P_8021Q); 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, 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.txd_cnt = NFP_NET_TX_DESCS_DEFAULT;
nn->dp.rxd_cnt = NFP_NET_RX_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->reconfig_lock);
spin_lock_init(&nn->link_status_lock); spin_lock_init(&nn->link_status_lock);
@ -3677,6 +3722,9 @@ nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev,
void nfp_net_free(struct nfp_net *nn) void nfp_net_free(struct nfp_net *nn)
{ {
WARN_ON(timer_pending(&nn->reconfig_timer) || nn->reconfig_posted); WARN_ON(timer_pending(&nn->reconfig_timer) || nn->reconfig_posted);
mutex_destroy(&nn->bar_lock);
if (nn->dp.netdev) if (nn->dp.netdev)
free_netdev(nn->dp.netdev); free_netdev(nn->dp.netdev);
else else

View File

@ -389,7 +389,6 @@
#define NFP_NET_CFG_MBOX_SIMPLE_CMD 0x0 #define NFP_NET_CFG_MBOX_SIMPLE_CMD 0x0
#define NFP_NET_CFG_MBOX_SIMPLE_RET 0x4 #define NFP_NET_CFG_MBOX_SIMPLE_RET 0x4
#define NFP_NET_CFG_MBOX_SIMPLE_VAL 0x8 #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_ADD 1
#define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2 #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, int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
struct nfp_net_tlv_caps *caps); 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_ */ #endif /* _NFP_NET_CTRL_H_ */