[SCSI] zfcp: Replace global config_lock with local list locks

The global config_lock was used to protect the configuration organized
in independent lists. It is not necessary to have a lock on driver
level for this purpose.  This patch replaces the global config_lock
with a set of local list locks.

Signed-off-by: Swen Schillig <[email protected]>
Signed-off-by: Christof Schmitt <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 2889e5f..883e139 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -187,7 +187,6 @@
 		goto out_gid_cache;
 
 	mutex_init(&zfcp_data.config_mutex);
-	rwlock_init(&zfcp_data.config_lock);
 
 	zfcp_data.scsi_transport_template =
 		fc_attach_transport(&zfcp_transport_functions);
@@ -238,12 +237,18 @@
  */
 struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
 {
+	unsigned long flags;
 	struct zfcp_unit *unit;
 
-	list_for_each_entry(unit, &port->unit_list_head, list)
+	read_lock_irqsave(&port->unit_list_lock, flags);
+	list_for_each_entry(unit, &port->unit_list, list)
 		if ((unit->fcp_lun == fcp_lun) &&
-		    !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE))
-		    return unit;
+		    !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE)) {
+			zfcp_unit_get(unit);
+			read_unlock_irqrestore(&port->unit_list_lock, flags);
+			return unit;
+		}
+	read_unlock_irqrestore(&port->unit_list_lock, flags);
 	return NULL;
 }
 
@@ -257,12 +262,18 @@
 struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
 					u64 wwpn)
 {
+	unsigned long flags;
 	struct zfcp_port *port;
 
-	list_for_each_entry(port, &adapter->port_list_head, list)
+	read_lock_irqsave(&adapter->port_list_lock, flags);
+	list_for_each_entry(port, &adapter->port_list, list)
 		if ((port->wwpn == wwpn) &&
-		    !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE))
+		    !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE)) {
+			zfcp_port_get(port);
+			read_unlock_irqrestore(&adapter->port_list_lock, flags);
 			return port;
+		}
+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
 	return NULL;
 }
 
@@ -284,12 +295,11 @@
 {
 	struct zfcp_unit *unit;
 
-	read_lock_irq(&zfcp_data.config_lock);
-	if (zfcp_get_unit_by_lun(port, fcp_lun)) {
-		read_unlock_irq(&zfcp_data.config_lock);
+	unit = zfcp_get_unit_by_lun(port, fcp_lun);
+	if (unit) {
+		zfcp_unit_put(unit);
 		return ERR_PTR(-EINVAL);
 	}
-	read_unlock_irq(&zfcp_data.config_lock);
 
 	unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
 	if (!unit)
@@ -335,13 +345,13 @@
 
 	zfcp_unit_get(unit);
 
-	write_lock_irq(&zfcp_data.config_lock);
-	list_add_tail(&unit->list, &port->unit_list_head);
+	write_lock_irq(&port->unit_list_lock);
+	list_add_tail(&unit->list, &port->unit_list);
+	write_unlock_irq(&port->unit_list_lock);
+
 	atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
 	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
 
-	write_unlock_irq(&zfcp_data.config_lock);
-
 	zfcp_port_get(port);
 
 	return unit;
@@ -356,11 +366,11 @@
  */
 void zfcp_unit_dequeue(struct zfcp_unit *unit)
 {
+	struct zfcp_port *port = unit->port;
+
 	wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
-	write_lock_irq(&zfcp_data.config_lock);
-	list_del(&unit->list);
-	write_unlock_irq(&zfcp_data.config_lock);
-	zfcp_port_put(unit->port);
+	list_del(&unit->list); /* no list locking required */
+	zfcp_port_put(port);
 	sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs);
 	device_unregister(&unit->sysfs_device);
 }
@@ -539,11 +549,13 @@
 	if (zfcp_fc_gs_setup(adapter))
 		goto generic_services_failed;
 
+	rwlock_init(&adapter->port_list_lock);
+	INIT_LIST_HEAD(&adapter->port_list);
+
 	init_waitqueue_head(&adapter->remove_wq);
 	init_waitqueue_head(&adapter->erp_ready_wq);
 	init_waitqueue_head(&adapter->erp_done_wqh);
 
-	INIT_LIST_HEAD(&adapter->port_list_head);
 	INIT_LIST_HEAD(&adapter->erp_ready_head);
 	INIT_LIST_HEAD(&adapter->erp_running_head);
 
@@ -650,19 +662,20 @@
 {
 	struct zfcp_port *port;
 
-	read_lock_irq(&zfcp_data.config_lock);
-	if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
-		read_unlock_irq(&zfcp_data.config_lock);
-		return ERR_PTR(-EINVAL);
+	port = zfcp_get_port_by_wwpn(adapter, wwpn);
+	if (port) {
+		zfcp_port_put(port);
+		return ERR_PTR(-EEXIST);
 	}
-	read_unlock_irq(&zfcp_data.config_lock);
 
 	port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
 	if (!port)
 		return ERR_PTR(-ENOMEM);
 
+	rwlock_init(&port->unit_list_lock);
+	INIT_LIST_HEAD(&port->unit_list);
+
 	init_waitqueue_head(&port->remove_wq);
-	INIT_LIST_HEAD(&port->unit_list_head);
 	INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup);
 	INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
 	INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);
@@ -698,13 +711,13 @@
 
 	zfcp_port_get(port);
 
-	write_lock_irq(&zfcp_data.config_lock);
-	list_add_tail(&port->list, &adapter->port_list_head);
+	write_lock_irq(&adapter->port_list_lock);
+	list_add_tail(&port->list, &adapter->port_list);
+	write_unlock_irq(&adapter->port_list_lock);
+
 	atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
 	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
 
-	write_unlock_irq(&zfcp_data.config_lock);
-
 	zfcp_adapter_get(adapter);
 	return port;
 }
@@ -715,12 +728,11 @@
  */
 void zfcp_port_dequeue(struct zfcp_port *port)
 {
-	write_lock_irq(&zfcp_data.config_lock);
-	list_del(&port->list);
-	write_unlock_irq(&zfcp_data.config_lock);
+	struct zfcp_adapter *adapter = port->adapter;
+
+	list_del(&port->list); /* no list locking required here */
 	wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
-	cancel_work_sync(&port->rport_work); /* usually not necessary */
-	zfcp_adapter_put(port->adapter);
+	zfcp_adapter_put(adapter);
 	sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs);
 	device_unregister(&port->sysfs_device);
 }