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

hsr: implement dellink to clean up resources

hsr_link_ops implements ->newlink() but not ->dellink(),
which leads that resources not released after removing the device,
particularly the entries in self_node_db and node_db.

So add ->dellink() implementation to replace the priv_destructor.
This also makes the code slightly easier to understand.

Reported-by: syzbot+c6167ec3de7def23d1e8@syzkaller.appspotmail.com
Cc: Arvid Brodin <arvid.brodin@alten.se>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Cong Wang and committed by
David S. Miller
b9a1e627 619afef0

+25 -10
+5 -8
net/hsr/hsr_device.c
··· 344 344 rcu_read_unlock(); 345 345 } 346 346 347 - /* According to comments in the declaration of struct net_device, this function 348 - * is "Called from unregister, can be used to call free_netdev". Ok then... 349 - */ 350 - static void hsr_dev_destroy(struct net_device *hsr_dev) 347 + void hsr_dev_destroy(struct net_device *hsr_dev) 351 348 { 352 349 struct hsr_priv *hsr; 353 350 struct hsr_port *port; ··· 354 357 355 358 hsr_debugfs_term(hsr); 356 359 357 - rtnl_lock(); 358 360 list_for_each_entry_safe(port, tmp, &hsr->ports, port_list) 359 361 hsr_del_port(port); 360 - rtnl_unlock(); 361 362 362 363 del_timer_sync(&hsr->prune_timer); 363 364 del_timer_sync(&hsr->announce_timer); 364 365 365 366 synchronize_rcu(); 367 + 368 + hsr_del_self_node(&hsr->self_node_db); 369 + hsr_del_nodes(&hsr->node_db); 366 370 } 367 371 368 372 static const struct net_device_ops hsr_device_ops = { ··· 390 392 dev->priv_flags |= IFF_NO_QUEUE; 391 393 392 394 dev->needs_free_netdev = true; 393 - dev->priv_destructor = hsr_dev_destroy; 394 395 395 396 dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | 396 397 NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | ··· 494 497 list_for_each_entry_safe(port, tmp, &hsr->ports, port_list) 495 498 hsr_del_port(port); 496 499 err_add_port: 497 - hsr_del_node(&hsr->self_node_db); 500 + hsr_del_self_node(&hsr->self_node_db); 498 501 499 502 return res; 500 503 }
+1
net/hsr/hsr_device.h
··· 14 14 void hsr_dev_setup(struct net_device *dev); 15 15 int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], 16 16 unsigned char multicast_spec, u8 protocol_version); 17 + void hsr_dev_destroy(struct net_device *hsr_dev); 17 18 void hsr_check_carrier_and_operstate(struct hsr_priv *hsr); 18 19 bool is_hsr_master(struct net_device *dev); 19 20 int hsr_get_max_mtu(struct hsr_priv *hsr);
+10 -1
net/hsr/hsr_framereg.c
··· 104 104 return 0; 105 105 } 106 106 107 - void hsr_del_node(struct list_head *self_node_db) 107 + void hsr_del_self_node(struct list_head *self_node_db) 108 108 { 109 109 struct hsr_node *node; 110 110 ··· 115 115 list_del_rcu(&node->mac_list); 116 116 kfree(node); 117 117 } 118 + } 119 + 120 + void hsr_del_nodes(struct list_head *node_db) 121 + { 122 + struct hsr_node *node; 123 + struct hsr_node *tmp; 124 + 125 + list_for_each_entry_safe(node, tmp, node_db, mac_list) 126 + kfree(node); 118 127 } 119 128 120 129 /* Allocate an hsr_node and add it to node_db. 'addr' is the node's address_A;
+2 -1
net/hsr/hsr_framereg.h
··· 12 12 13 13 struct hsr_node; 14 14 15 - void hsr_del_node(struct list_head *self_node_db); 15 + void hsr_del_self_node(struct list_head *self_node_db); 16 + void hsr_del_nodes(struct list_head *node_db); 16 17 struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[], 17 18 u16 seq_out); 18 19 struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
+7
net/hsr/hsr_netlink.c
··· 69 69 return hsr_dev_finalize(dev, link, multicast_spec, hsr_version); 70 70 } 71 71 72 + static void hsr_dellink(struct net_device *hsr_dev, struct list_head *head) 73 + { 74 + hsr_dev_destroy(hsr_dev); 75 + unregister_netdevice_queue(hsr_dev, head); 76 + } 77 + 72 78 static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev) 73 79 { 74 80 struct hsr_priv *hsr; ··· 119 113 .priv_size = sizeof(struct hsr_priv), 120 114 .setup = hsr_dev_setup, 121 115 .newlink = hsr_newlink, 116 + .dellink = hsr_dellink, 122 117 .fill_info = hsr_fill_info, 123 118 }; 124 119