[SCSI] bnx2fc: Handle NETDEV_UNREGISTER for vlan devices
Bhanu Prakash Gollapudi [Fri, 5 Aug 2011 00:38:42 +0000 (17:38 -0700)]
Since the driver holds the reference for vlan netdev, the reference has to be
released by the driver when the vlan device is removed. Driver handles this in
NETDEV_UNREGISTER event.

Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>

drivers/scsi/bnx2fc/bnx2fc_fcoe.c

index 673c97c..dccabaf 100644 (file)
@@ -56,6 +56,7 @@ static struct scsi_host_template bnx2fc_shost_template;
 static struct fc_function_template bnx2fc_transport_function;
 static struct fc_function_template bnx2fc_vport_xport_function;
 static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode);
+static void __bnx2fc_destroy(struct bnx2fc_interface *interface, bool schedule);
 static int bnx2fc_destroy(struct net_device *net_device);
 static int bnx2fc_enable(struct net_device *netdev);
 static int bnx2fc_disable(struct net_device *netdev);
@@ -78,6 +79,7 @@ static void bnx2fc_destroy_work(struct work_struct *work);
 static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device *phys_dev);
 static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
                                                        *phys_dev);
+static inline void bnx2fc_interface_put(struct bnx2fc_interface *interface);
 static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic);
 
 static int bnx2fc_fw_init(struct bnx2fc_hba *hba);
@@ -783,7 +785,7 @@ static void bnx2fc_destroy_timer(unsigned long data)
  * @vlan_id:   vlan id - associated vlan id with this event
  *
  * Handles NETDEV_UP, NETDEV_DOWN, NETDEV_GOING_DOWN,NETDEV_CHANGE and
- * NETDEV_CHANGE_MTU events
+ * NETDEV_CHANGE_MTU events. Handle NETDEV_UNREGISTER only for vlans.
  */
 static void bnx2fc_indicate_netevent(void *context, unsigned long event,
                                     u16 vlan_id)
@@ -791,12 +793,11 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
        struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context;
        struct fc_lport *lport;
        struct fc_lport *vport;
-       struct bnx2fc_interface *interface;
+       struct bnx2fc_interface *interface, *tmp;
        int wait_for_upload = 0;
        u32 link_possible = 1;
 
-       /* Ignore vlans for now */
-       if (vlan_id != 0)
+       if (vlan_id != 0 && event != NETDEV_UNREGISTER)
                return;
 
        switch (event) {
@@ -820,6 +821,18 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
        case NETDEV_CHANGE:
                break;
 
+       case NETDEV_UNREGISTER:
+               if (!vlan_id)
+                       return;
+               mutex_lock(&bnx2fc_dev_lock);
+               list_for_each_entry_safe(interface, tmp, &if_list, list) {
+                       if (interface->hba != hba)
+                               continue;
+                       __bnx2fc_destroy(interface, true);
+               }
+               mutex_unlock(&bnx2fc_dev_lock);
+               return;
+
        default:
                printk(KERN_ERR PFX "Unkonwn netevent %ld", event);
                return;
@@ -1022,12 +1035,27 @@ static int bnx2fc_vport_create(struct fc_vport *vport, bool disabled)
        return 0;
 }
 
+static void bnx2fc_free_vport(struct bnx2fc_hba *hba, struct fc_lport *lport)
+{
+       struct bnx2fc_lport *blport, *tmp;
+
+       spin_lock_bh(&hba->hba_lock);
+       list_for_each_entry_safe(blport, tmp, &hba->vports, list) {
+               if (blport->lport == lport) {
+                       list_del(&blport->list);
+                       kfree(blport);
+               }
+       }
+       spin_unlock_bh(&hba->hba_lock);
+}
+
 static int bnx2fc_vport_destroy(struct fc_vport *vport)
 {
        struct Scsi_Host *shost = vport_to_shost(vport);
        struct fc_lport *n_port = shost_priv(shost);
        struct fc_lport *vn_port = vport->dd_data;
        struct fcoe_port *port = lport_priv(vn_port);
+       struct bnx2fc_interface *interface = port->priv;
        struct fc_lport *v_port;
        bool found = false;
 
@@ -1044,6 +1072,9 @@ static int bnx2fc_vport_destroy(struct fc_vport *vport)
        }
        list_del(&vn_port->list);
        mutex_unlock(&n_port->lp_mutex);
+       bnx2fc_free_vport(interface->hba, port->lport);
+       bnx2fc_port_shutdown(port->lport);
+       bnx2fc_interface_put(interface);
        queue_work(bnx2fc_wq, &port->destroy_work);
        return 0;
 }
@@ -1386,7 +1417,6 @@ static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface)
 {
        struct fc_lport *lport = interface->ctlr.lp;
        struct fcoe_port *port = lport_priv(lport);
-       struct bnx2fc_lport *blport, *tmp;
        struct bnx2fc_hba *hba = interface->hba;
 
        /* Stop the transmit retry timer */
@@ -1400,14 +1430,7 @@ static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface)
        __dev_remove_pack(&interface->fip_packet_type);
        synchronize_net();
 
-       spin_lock_bh(&hba->hba_lock);
-       list_for_each_entry_safe(blport, tmp, &hba->vports, list) {
-               if (blport->lport == lport) {
-                       list_del(&blport->list);
-                       kfree(blport);
-               }
-       }
-       spin_unlock_bh(&hba->hba_lock);
+       bnx2fc_free_vport(hba, lport);
 }
 
 static void bnx2fc_if_destroy(struct fc_lport *lport)
@@ -1433,6 +1456,23 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
        scsi_host_put(lport->host);
 }
 
+static void __bnx2fc_destroy(struct bnx2fc_interface *interface, bool schedule)
+{
+       struct fc_lport *lport = interface->ctlr.lp;
+       struct fcoe_port *port = lport_priv(lport);
+
+       bnx2fc_interface_cleanup(interface);
+       bnx2fc_stop(interface);
+
+       list_del(&interface->list);
+       lport = interface->ctlr.lp;
+       bnx2fc_interface_put(interface);
+       if (schedule)
+               queue_work(bnx2fc_wq, &port->destroy_work);
+       else
+               bnx2fc_if_destroy(lport);
+}
+
 /**
  * bnx2fc_destroy - Destroy a bnx2fc FCoE interface
  *
@@ -1446,7 +1486,6 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
 static int bnx2fc_destroy(struct net_device *netdev)
 {
        struct bnx2fc_interface *interface = NULL;
-       struct fc_lport *lport;
        int rc = 0;
 
        rtnl_lock();
@@ -1460,13 +1499,8 @@ static int bnx2fc_destroy(struct net_device *netdev)
        }
 
 
-       bnx2fc_interface_cleanup(interface);
-       lport = interface->ctlr.lp;
-       bnx2fc_stop(interface);
-       list_del(&interface->list);
        destroy_workqueue(interface->timer_work_queue);
-       bnx2fc_interface_put(interface);
-       bnx2fc_if_destroy(lport);
+       __bnx2fc_destroy(interface, false);
 
 netdev_err:
        mutex_unlock(&bnx2fc_dev_lock);
@@ -1478,15 +1512,12 @@ static void bnx2fc_destroy_work(struct work_struct *work)
 {
        struct fcoe_port *port;
        struct fc_lport *lport;
-       struct bnx2fc_interface *interface;
 
        port = container_of(work, struct fcoe_port, destroy_work);
        lport = port->lport;
-       interface = port->priv;
 
        BNX2FC_HBA_DBG(lport, "Entered bnx2fc_destroy_work\n");
 
-       bnx2fc_port_shutdown(lport);
        rtnl_lock();
        mutex_lock(&bnx2fc_dev_lock);
        bnx2fc_if_destroy(lport);
@@ -2031,7 +2062,6 @@ static void bnx2fc_ulp_exit(struct cnic_dev *dev)
 {
        struct bnx2fc_hba *hba;
        struct bnx2fc_interface *interface, *tmp;
-       struct fc_lport *lport;
 
        BNX2FC_MISC_DBG("Entered bnx2fc_ulp_exit\n");
 
@@ -2053,18 +2083,10 @@ static void bnx2fc_ulp_exit(struct cnic_dev *dev)
        list_del_init(&hba->list);
        adapter_count--;
 
-       list_for_each_entry_safe(interface, tmp, &if_list, list) {
+       list_for_each_entry_safe(interface, tmp, &if_list, list)
                /* destroy not called yet, move to quiesced list */
-               if (interface->hba == hba) {
-                       bnx2fc_interface_cleanup(interface);
-                       bnx2fc_stop(interface);
-
-                       list_del(&interface->list);
-                       lport = interface->ctlr.lp;
-                       bnx2fc_interface_put(interface);
-                       bnx2fc_if_destroy(lport);
-               }
-       }
+               if (interface->hba == hba)
+                       __bnx2fc_destroy(interface, false);
        mutex_unlock(&bnx2fc_dev_lock);
 
        bnx2fc_ulp_stop(hba);