Commit 0ad722bd authored by Hui Zhou's avatar Hui Zhou Committed by David S. Miller

nfp: flower: fix for take a mutex lock in soft irq context and rcu lock

The neighbour event callback call the function nfp_tun_write_neigh,
this function will take a mutex lock and it is in soft irq context,
change the work queue to process the neighbour event.

Move the nfp_tun_write_neigh function out of range rcu_read_lock/unlock()
in function nfp_tunnel_request_route_v4 and nfp_tunnel_request_route_v6.

Fixes: abc21095 ("nfp: flower: tunnel neigh support bond offload")
CC: stable@vger.kernel.org # 6.2+
Signed-off-by: default avatarHui Zhou <hui.zhou@corigine.com>
Signed-off-by: default avatarLouis Peens <louis.peens@corigine.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 803a809d
...@@ -160,6 +160,18 @@ struct nfp_tun_mac_addr_offload { ...@@ -160,6 +160,18 @@ struct nfp_tun_mac_addr_offload {
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
}; };
/**
* struct nfp_neigh_update_work - update neighbour information to nfp
* @work: Work queue for writing neigh to the nfp
* @n: neighbour entry
* @app: Back pointer to app
*/
struct nfp_neigh_update_work {
struct work_struct work;
struct neighbour *n;
struct nfp_app *app;
};
enum nfp_flower_mac_offload_cmd { enum nfp_flower_mac_offload_cmd {
NFP_TUNNEL_MAC_OFFLOAD_ADD = 0, NFP_TUNNEL_MAC_OFFLOAD_ADD = 0,
NFP_TUNNEL_MAC_OFFLOAD_DEL = 1, NFP_TUNNEL_MAC_OFFLOAD_DEL = 1,
...@@ -607,38 +619,30 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, ...@@ -607,38 +619,30 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app,
nfp_flower_cmsg_warn(app, "Neighbour configuration failed.\n"); nfp_flower_cmsg_warn(app, "Neighbour configuration failed.\n");
} }
static int static void
nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, nfp_tun_release_neigh_update_work(struct nfp_neigh_update_work *update_work)
void *ptr)
{ {
struct nfp_flower_priv *app_priv; neigh_release(update_work->n);
struct netevent_redirect *redir; kfree(update_work);
struct neighbour *n; }
static void nfp_tun_neigh_update(struct work_struct *work)
{
struct nfp_neigh_update_work *update_work;
struct nfp_app *app; struct nfp_app *app;
struct neighbour *n;
bool neigh_invalid; bool neigh_invalid;
int err; int err;
switch (event) { update_work = container_of(work, struct nfp_neigh_update_work, work);
case NETEVENT_REDIRECT: app = update_work->app;
redir = (struct netevent_redirect *)ptr; n = update_work->n;
n = redir->neigh;
break;
case NETEVENT_NEIGH_UPDATE:
n = (struct neighbour *)ptr;
break;
default:
return NOTIFY_DONE;
}
neigh_invalid = !(n->nud_state & NUD_VALID) || n->dead;
app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb);
app = app_priv->app;
if (!nfp_flower_get_port_id_from_netdev(app, n->dev)) if (!nfp_flower_get_port_id_from_netdev(app, n->dev))
return NOTIFY_DONE; goto out;
#if IS_ENABLED(CONFIG_INET) #if IS_ENABLED(CONFIG_INET)
neigh_invalid = !(n->nud_state & NUD_VALID) || n->dead;
if (n->tbl->family == AF_INET6) { if (n->tbl->family == AF_INET6) {
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
struct flowi6 flow6 = {}; struct flowi6 flow6 = {};
...@@ -655,13 +659,11 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, ...@@ -655,13 +659,11 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event,
dst = ip6_dst_lookup_flow(dev_net(n->dev), NULL, dst = ip6_dst_lookup_flow(dev_net(n->dev), NULL,
&flow6, NULL); &flow6, NULL);
if (IS_ERR(dst)) if (IS_ERR(dst))
return NOTIFY_DONE; goto out;
dst_release(dst); dst_release(dst);
} }
nfp_tun_write_neigh(n->dev, app, &flow6, n, true, false); nfp_tun_write_neigh(n->dev, app, &flow6, n, true, false);
#else
return NOTIFY_DONE;
#endif /* CONFIG_IPV6 */ #endif /* CONFIG_IPV6 */
} else { } else {
struct flowi4 flow4 = {}; struct flowi4 flow4 = {};
...@@ -678,17 +680,71 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, ...@@ -678,17 +680,71 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event,
rt = ip_route_output_key(dev_net(n->dev), &flow4); rt = ip_route_output_key(dev_net(n->dev), &flow4);
err = PTR_ERR_OR_ZERO(rt); err = PTR_ERR_OR_ZERO(rt);
if (err) if (err)
return NOTIFY_DONE; goto out;
ip_rt_put(rt); ip_rt_put(rt);
} }
nfp_tun_write_neigh(n->dev, app, &flow4, n, false, false); nfp_tun_write_neigh(n->dev, app, &flow4, n, false, false);
} }
#else
return NOTIFY_DONE;
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
out:
nfp_tun_release_neigh_update_work(update_work);
}
return NOTIFY_OK; static struct nfp_neigh_update_work *
nfp_tun_alloc_neigh_update_work(struct nfp_app *app, struct neighbour *n)
{
struct nfp_neigh_update_work *update_work;
update_work = kzalloc(sizeof(*update_work), GFP_ATOMIC);
if (!update_work)
return NULL;
INIT_WORK(&update_work->work, nfp_tun_neigh_update);
neigh_hold(n);
update_work->n = n;
update_work->app = app;
return update_work;
}
static int
nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct nfp_neigh_update_work *update_work;
struct nfp_flower_priv *app_priv;
struct netevent_redirect *redir;
struct neighbour *n;
struct nfp_app *app;
switch (event) {
case NETEVENT_REDIRECT:
redir = (struct netevent_redirect *)ptr;
n = redir->neigh;
break;
case NETEVENT_NEIGH_UPDATE:
n = (struct neighbour *)ptr;
break;
default:
return NOTIFY_DONE;
}
#if IS_ENABLED(CONFIG_IPV6)
if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl)
#else
if (n->tbl != &arp_tbl)
#endif
return NOTIFY_DONE;
app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb);
app = app_priv->app;
update_work = nfp_tun_alloc_neigh_update_work(app, n);
if (!update_work)
return NOTIFY_DONE;
queue_work(system_highpri_wq, &update_work->work);
return NOTIFY_DONE;
} }
void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb) void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb)
...@@ -706,6 +762,7 @@ void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb) ...@@ -706,6 +762,7 @@ void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb)
netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL); netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL);
if (!netdev) if (!netdev)
goto fail_rcu_unlock; goto fail_rcu_unlock;
dev_hold(netdev);
flow.daddr = payload->ipv4_addr; flow.daddr = payload->ipv4_addr;
flow.flowi4_proto = IPPROTO_UDP; flow.flowi4_proto = IPPROTO_UDP;
...@@ -725,13 +782,16 @@ void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb) ...@@ -725,13 +782,16 @@ void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb)
ip_rt_put(rt); ip_rt_put(rt);
if (!n) if (!n)
goto fail_rcu_unlock; goto fail_rcu_unlock;
rcu_read_unlock();
nfp_tun_write_neigh(n->dev, app, &flow, n, false, true); nfp_tun_write_neigh(n->dev, app, &flow, n, false, true);
neigh_release(n); neigh_release(n);
rcu_read_unlock(); dev_put(netdev);
return; return;
fail_rcu_unlock: fail_rcu_unlock:
rcu_read_unlock(); rcu_read_unlock();
dev_put(netdev);
nfp_flower_cmsg_warn(app, "Requested route not found.\n"); nfp_flower_cmsg_warn(app, "Requested route not found.\n");
} }
...@@ -749,6 +809,7 @@ void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb) ...@@ -749,6 +809,7 @@ void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb)
netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL); netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL);
if (!netdev) if (!netdev)
goto fail_rcu_unlock; goto fail_rcu_unlock;
dev_hold(netdev);
flow.daddr = payload->ipv6_addr; flow.daddr = payload->ipv6_addr;
flow.flowi6_proto = IPPROTO_UDP; flow.flowi6_proto = IPPROTO_UDP;
...@@ -766,14 +827,16 @@ void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb) ...@@ -766,14 +827,16 @@ void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb)
dst_release(dst); dst_release(dst);
if (!n) if (!n)
goto fail_rcu_unlock; goto fail_rcu_unlock;
rcu_read_unlock();
nfp_tun_write_neigh(n->dev, app, &flow, n, true, true); nfp_tun_write_neigh(n->dev, app, &flow, n, true, true);
neigh_release(n); neigh_release(n);
rcu_read_unlock(); dev_put(netdev);
return; return;
fail_rcu_unlock: fail_rcu_unlock:
rcu_read_unlock(); rcu_read_unlock();
dev_put(netdev);
nfp_flower_cmsg_warn(app, "Requested IPv6 route not found.\n"); nfp_flower_cmsg_warn(app, "Requested IPv6 route not found.\n");
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment