--- zzzz-none-000/linux-3.10.107/drivers/net/caif/caif_serial.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/net/caif/caif_serial.c 2021-02-04 17:41:59.000000000 +0000 @@ -35,8 +35,9 @@ #define OFF 0 #define CAIF_MAX_MTU 4096 -/*This list is protected by the rtnl lock. */ +static DEFINE_SPINLOCK(ser_lock); static LIST_HEAD(ser_list); +static LIST_HEAD(ser_release_list); static bool ser_loop; module_param(ser_loop, bool, S_IRUGO); @@ -69,7 +70,6 @@ struct tty_struct *tty; bool tx_started; unsigned long state; - char *tty_name; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_tty_dir; struct debugfs_blob_wrapper tx_blob; @@ -203,7 +203,6 @@ skb->protocol = htons(ETH_P_CAIF); skb_reset_mac_header(skb); - skb->dev = ser->dev; debugfs_rx(ser, data, count); /* Push received packet up the stack. */ ret = netif_rx_ni(skb); @@ -308,6 +307,28 @@ } +static void ser_release(struct work_struct *work) +{ + struct list_head list; + struct ser_device *ser, *tmp; + + spin_lock(&ser_lock); + list_replace_init(&ser_release_list, &list); + spin_unlock(&ser_lock); + + if (!list_empty(&list)) { + rtnl_lock(); + list_for_each_entry_safe(ser, tmp, &list, node) { + dev_close(ser->dev); + unregister_netdevice(ser->dev); + debugfs_deinit(ser); + } + rtnl_unlock(); + } +} + +static DECLARE_WORK(ser_release_work, ser_release); + static int ldisc_open(struct tty_struct *tty) { struct ser_device *ser; @@ -321,8 +342,14 @@ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) return -EPERM; - sprintf(name, "cf%s", tty->name); - dev = alloc_netdev(sizeof(*ser), name, caifdev_setup); + /* release devices to avoid name collision */ + ser_release(NULL); + + result = snprintf(name, sizeof(name), "cf%s", tty->name); + if (result >= IFNAMSIZ) + return -EINVAL; + dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, + caifdev_setup); if (!dev) return -ENOMEM; @@ -341,7 +368,9 @@ return -ENODEV; } + spin_lock(&ser_lock); list_add(&ser->node, &ser_list); + spin_unlock(&ser_lock); rtnl_unlock(); netif_stop_queue(dev); update_tty_status(ser); @@ -351,19 +380,13 @@ static void ldisc_close(struct tty_struct *tty) { struct ser_device *ser = tty->disc_data; - /* Remove may be called inside or outside of rtnl_lock */ - int islocked = rtnl_is_locked(); - if (!islocked) - rtnl_lock(); - /* device is freed automagically by net-sysfs */ - dev_close(ser->dev); - unregister_netdevice(ser->dev); - list_del(&ser->node); - debugfs_deinit(ser); tty_kref_put(ser->tty); - if (!islocked) - rtnl_unlock(); + + spin_lock(&ser_lock); + list_move(&ser->node, &ser_release_list); + spin_unlock(&ser_lock); + schedule_work(&ser_release_work); } /* The line discipline structure. */ @@ -404,7 +427,7 @@ dev->type = ARPHRD_CAIF; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->mtu = CAIF_MAX_MTU; - dev->tx_queue_len = 0; + dev->priv_flags |= IFF_NO_QUEUE; dev->destructor = free_netdev; skb_queue_head_init(&serdev->head); serdev->common.link_select = CAIF_LINK_LOW_LATENCY; @@ -438,16 +461,11 @@ static void __exit caif_ser_exit(void) { - struct ser_device *ser = NULL; - struct list_head *node; - struct list_head *_tmp; - - list_for_each_safe(node, _tmp, &ser_list) { - ser = list_entry(node, struct ser_device, node); - dev_close(ser->dev); - unregister_netdevice(ser->dev); - list_del(node); - } + spin_lock(&ser_lock); + list_splice(&ser_list, &ser_release_list); + spin_unlock(&ser_lock); + ser_release(NULL); + cancel_work_sync(&ser_release_work); tty_unregister_ldisc(N_CAIF); debugfs_remove_recursive(debugfsdir); }