Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: openvswitch: fixes potential deadlock in dp cleanup code

The previous patch introduced a deadlock, this patch fixes it by making
sure the work is canceled without holding the global ovs lock. This is
done by moving the reorder processing one layer up to the netns level.

Fixes: eac87c413bf9 ("net: openvswitch: reorder masks array based on usage")
Reported-by: syzbot+2c4ff3614695f75ce26c@syzkaller.appspotmail.com
Reported-by: syzbot+bad6507e5db05017b008@syzkaller.appspotmail.com
Reviewed-by: Paolo <pabeni@redhat.com>
Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eelco Chaudron and committed by
David S. Miller
a65878d6 dfd3d526

+13 -14
+12 -11
net/openvswitch/datapath.c
··· 1655 1655 goto err_destroy_reply; 1656 1656 1657 1657 ovs_dp_set_net(dp, sock_net(skb->sk)); 1658 - INIT_DELAYED_WORK(&dp->masks_rebalance, ovs_dp_masks_rebalance); 1659 1658 1660 1659 /* Allocate table. */ 1661 1660 err = ovs_flow_tbl_init(&dp->table); ··· 1714 1715 ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id); 1715 1716 list_add_tail_rcu(&dp->list_node, &ovs_net->dps); 1716 1717 1717 - schedule_delayed_work(&dp->masks_rebalance, 1718 - msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL)); 1719 - 1720 1718 ovs_unlock(); 1721 1719 1722 1720 ovs_notify(&dp_datapath_genl_family, reply, info); ··· 1758 1762 1759 1763 /* RCU destroy the flow table */ 1760 1764 call_rcu(&dp->rcu, destroy_dp_rcu); 1761 - 1762 - /* Cancel remaining work. */ 1763 - cancel_delayed_work_sync(&dp->masks_rebalance); 1764 1765 } 1765 1766 1766 1767 static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) ··· 2342 2349 2343 2350 static void ovs_dp_masks_rebalance(struct work_struct *work) 2344 2351 { 2345 - struct datapath *dp = container_of(work, struct datapath, 2346 - masks_rebalance.work); 2352 + struct ovs_net *ovs_net = container_of(work, struct ovs_net, 2353 + masks_rebalance.work); 2354 + struct datapath *dp; 2347 2355 2348 2356 ovs_lock(); 2349 - ovs_flow_masks_rebalance(&dp->table); 2357 + 2358 + list_for_each_entry(dp, &ovs_net->dps, list_node) 2359 + ovs_flow_masks_rebalance(&dp->table); 2360 + 2350 2361 ovs_unlock(); 2351 2362 2352 - schedule_delayed_work(&dp->masks_rebalance, 2363 + schedule_delayed_work(&ovs_net->masks_rebalance, 2353 2364 msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL)); 2354 2365 } 2355 2366 ··· 2451 2454 2452 2455 INIT_LIST_HEAD(&ovs_net->dps); 2453 2456 INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq); 2457 + INIT_DELAYED_WORK(&ovs_net->masks_rebalance, ovs_dp_masks_rebalance); 2458 + schedule_delayed_work(&ovs_net->masks_rebalance, 2459 + msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL)); 2454 2460 return ovs_ct_init(net); 2455 2461 } 2456 2462 ··· 2508 2508 2509 2509 ovs_unlock(); 2510 2510 2511 + cancel_delayed_work_sync(&ovs_net->masks_rebalance); 2511 2512 cancel_work_sync(&ovs_net->dp_notify_work); 2512 2513 } 2513 2514
+1 -3
net/openvswitch/datapath.h
··· 84 84 85 85 /* Switch meters. */ 86 86 struct dp_meter_table meter_tbl; 87 - 88 - /* re-balance flow masks timer */ 89 - struct delayed_work masks_rebalance; 90 87 }; 91 88 92 89 /** ··· 132 135 struct ovs_net { 133 136 struct list_head dps; 134 137 struct work_struct dp_notify_work; 138 + struct delayed_work masks_rebalance; 135 139 #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) 136 140 struct ovs_ct_limit_info *ct_limit_info; 137 141 #endif