Skip to content

Commit 469998c

Browse files
Vadim Lomovtsevdavem330
authored andcommitted
net: thunderx: prevent concurrent data re-writing by nicvf_set_rx_mode
For each network interface linux network stack issue ndo_set_rx_mode call in order to configure MAC address filters (e.g. for multicast filtering). Currently ThunderX NICVF driver has only one ordered workqueue to process such requests for all VFs. And because of that it is possible that subsequent call to ndo_set_rx_mode would corrupt data which is currently in use by nicvf_set_rx_mode_task. Which in turn could cause following issue: [...] [ 48.978341] Unable to handle kernel paging request at virtual address 1fffff0000000000 [ 48.986275] Mem abort info: [ 48.989058] Exception class = DABT (current EL), IL = 32 bits [ 48.994965] SET = 0, FnV = 0 [ 48.998020] EA = 0, S1PTW = 0 [ 49.001152] Data abort info: [ 49.004022] ISV = 0, ISS = 0x00000004 [ 49.007869] CM = 0, WnR = 0 [ 49.010826] [1fffff0000000000] address between user and kernel address ranges [ 49.017963] Internal error: Oops: 96000004 [#1] SMP [...] [ 49.072138] task: ffff800fdd675400 task.stack: ffff000026440000 [ 49.078051] PC is at prefetch_freepointer.isra.37+0x28/0x3c [ 49.083613] LR is at kmem_cache_alloc_trace+0xc8/0x1fc [...] [ 49.272684] [<ffff0000082738f0>] prefetch_freepointer.isra.37+0x28/0x3c [ 49.279286] [<ffff000008276bc8>] kmem_cache_alloc_trace+0xc8/0x1fc [ 49.285455] [<ffff0000082c0c0c>] alloc_fdtable+0x78/0x134 [ 49.290841] [<ffff0000082c15c0>] dup_fd+0x254/0x2f4 [ 49.295709] [<ffff0000080d1954>] copy_process.isra.38.part.39+0x64c/0x1168 [ 49.302572] [<ffff0000080d264c>] _do_fork+0xfc/0x3b0 [ 49.307524] [<ffff0000080d29e8>] SyS_clone+0x44/0x50 [...] This patch is to prevent such concurrent data write with spinlock. Reported-by: Dean Nelson <[email protected]> Signed-off-by: Vadim Lomovtsev <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 909f1ed commit 469998c

File tree

2 files changed

+38
-14
lines changed

2 files changed

+38
-14
lines changed

drivers/net/ethernet/cavium/thunder/nic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@ struct nicvf {
325325
struct tasklet_struct qs_err_task;
326326
struct work_struct reset_task;
327327
struct nicvf_work rx_mode_work;
328+
/* spinlock to protect workqueue arguments from concurrent access */
329+
spinlock_t rx_mode_wq_lock;
328330

329331
/* PTP timestamp */
330332
struct cavium_ptp *ptp_clock;

drivers/net/ethernet/cavium/thunder/nicvf_main.c

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,17 +1923,12 @@ static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
19231923
}
19241924
}
19251925

1926-
static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
1926+
static void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs,
1927+
struct nicvf *nic)
19271928
{
1928-
struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
1929-
work.work);
1930-
struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
19311929
union nic_mbx mbx = {};
19321930
int idx;
19331931

1934-
if (!vf_work)
1935-
return;
1936-
19371932
/* From the inside of VM code flow we have only 128 bits memory
19381933
* available to send message to host's PF, so send all mc addrs
19391934
* one by one, starting from flush command in case if kernel
@@ -1944,7 +1939,7 @@ static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
19441939
mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
19451940
nicvf_send_msg_to_pf(nic, &mbx);
19461941

1947-
if (vf_work->mode & BGX_XCAST_MCAST_FILTER) {
1942+
if (mode & BGX_XCAST_MCAST_FILTER) {
19481943
/* once enabling filtering, we need to signal to PF to add
19491944
* its' own LMAC to the filter to accept packets for it.
19501945
*/
@@ -1954,23 +1949,46 @@ static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
19541949
}
19551950

19561951
/* check if we have any specific MACs to be added to PF DMAC filter */
1957-
if (vf_work->mc) {
1952+
if (mc_addrs) {
19581953
/* now go through kernel list of MACs and add them one by one */
1959-
for (idx = 0; idx < vf_work->mc->count; idx++) {
1954+
for (idx = 0; idx < mc_addrs->count; idx++) {
19601955
mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
1961-
mbx.xcast.data.mac = vf_work->mc->mc[idx];
1956+
mbx.xcast.data.mac = mc_addrs->mc[idx];
19621957
nicvf_send_msg_to_pf(nic, &mbx);
19631958
}
1964-
kfree(vf_work->mc);
1959+
kfree(mc_addrs);
19651960
}
19661961

19671962
/* and finally set rx mode for PF accordingly */
19681963
mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
1969-
mbx.xcast.data.mode = vf_work->mode;
1964+
mbx.xcast.data.mode = mode;
19701965

19711966
nicvf_send_msg_to_pf(nic, &mbx);
19721967
}
19731968

1969+
static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
1970+
{
1971+
struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
1972+
work.work);
1973+
struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
1974+
u8 mode;
1975+
struct xcast_addr_list *mc;
1976+
1977+
if (!vf_work)
1978+
return;
1979+
1980+
/* Save message data locally to prevent them from
1981+
* being overwritten by next ndo_set_rx_mode call().
1982+
*/
1983+
spin_lock(&nic->rx_mode_wq_lock);
1984+
mode = vf_work->mode;
1985+
mc = vf_work->mc;
1986+
vf_work->mc = NULL;
1987+
spin_unlock(&nic->rx_mode_wq_lock);
1988+
1989+
__nicvf_set_rx_mode_task(mode, mc, nic);
1990+
}
1991+
19741992
static void nicvf_set_rx_mode(struct net_device *netdev)
19751993
{
19761994
struct nicvf *nic = netdev_priv(netdev);
@@ -2004,9 +2022,12 @@ static void nicvf_set_rx_mode(struct net_device *netdev)
20042022
}
20052023
}
20062024
}
2025+
spin_lock(&nic->rx_mode_wq_lock);
2026+
kfree(nic->rx_mode_work.mc);
20072027
nic->rx_mode_work.mc = mc_list;
20082028
nic->rx_mode_work.mode = mode;
2009-
queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 2 * HZ);
2029+
queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 0);
2030+
spin_unlock(&nic->rx_mode_wq_lock);
20102031
}
20112032

20122033
static const struct net_device_ops nicvf_netdev_ops = {
@@ -2163,6 +2184,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
21632184
INIT_WORK(&nic->reset_task, nicvf_reset_task);
21642185

21652186
INIT_DELAYED_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
2187+
spin_lock_init(&nic->rx_mode_wq_lock);
21662188

21672189
err = register_netdev(netdev);
21682190
if (err) {

0 commit comments

Comments
 (0)