aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/scsi
diff options
context:
space:
mode:
authorSteffen Maier <maier@linux.vnet.ibm.com>2012-09-04 09:23:34 -0400
committerJames Bottomley <JBottomley@Parallels.com>2012-09-24 04:11:02 -0400
commitd99b601b63386f3395dc26a699ae703a273d9982 (patch)
tree208966f18e89775d68e82092c1ff648206fbd688 /drivers/s390/scsi
parentca579c9f136af4274ccfd1bcaee7f38a29a0e2e9 (diff)
[SCSI] zfcp: restore refcount check on port_remove
Upstream commit f3450c7b917201bb49d67032e9f60d5125675d6a "[SCSI] zfcp: Replace local reference counting with common kref" accidentally dropped a reference count check before tearing down zfcp_ports that are potentially in use by zfcp_units. Even remote ports in use can be removed causing unreachable garbage objects zfcp_ports with zfcp_units. Thus units won't come back even after a manual port_rescan. The kref of zfcp_port->dev.kobj is already used by the driver core. We cannot re-use it to track the number of zfcp_units. Re-introduce our own counter for units per port and check on port_remove. Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com> Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com> Cc: <stable@vger.kernel.org> #2.6.33+ Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/s390/scsi')
-rw-r--r--drivers/s390/scsi/zfcp_aux.c1
-rw-r--r--drivers/s390/scsi/zfcp_def.h1
-rw-r--r--drivers/s390/scsi/zfcp_ext.h1
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c18
-rw-r--r--drivers/s390/scsi/zfcp_unit.c36
5 files changed, 45 insertions, 12 deletions
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index aff8621de806..f6adde44f226 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -519,6 +519,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
519 519
520 rwlock_init(&port->unit_list_lock); 520 rwlock_init(&port->unit_list_lock);
521 INIT_LIST_HEAD(&port->unit_list); 521 INIT_LIST_HEAD(&port->unit_list);
522 atomic_set(&port->units, 0);
522 523
523 INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); 524 INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup);
524 INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); 525 INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 5bc4afba8b09..1305955cbf59 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -205,6 +205,7 @@ struct zfcp_port {
205 struct zfcp_adapter *adapter; /* adapter used to access port */ 205 struct zfcp_adapter *adapter; /* adapter used to access port */
206 struct list_head unit_list; /* head of logical unit list */ 206 struct list_head unit_list; /* head of logical unit list */
207 rwlock_t unit_list_lock; /* unit list lock */ 207 rwlock_t unit_list_lock; /* unit list lock */
208 atomic_t units; /* zfcp_unit count */
208 atomic_t status; /* status of this remote port */ 209 atomic_t status; /* status of this remote port */
209 u64 wwnn; /* WWNN if known */ 210 u64 wwnn; /* WWNN if known */
210 u64 wwpn; /* WWPN */ 211 u64 wwpn; /* WWPN */
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 4eeef78447f2..03441a7fb463 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -159,6 +159,7 @@ extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int);
159extern struct attribute_group zfcp_sysfs_unit_attrs; 159extern struct attribute_group zfcp_sysfs_unit_attrs;
160extern struct attribute_group zfcp_sysfs_adapter_attrs; 160extern struct attribute_group zfcp_sysfs_adapter_attrs;
161extern struct attribute_group zfcp_sysfs_port_attrs; 161extern struct attribute_group zfcp_sysfs_port_attrs;
162extern struct mutex zfcp_sysfs_port_units_mutex;
162extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; 163extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
163extern struct device_attribute *zfcp_sysfs_shost_attrs[]; 164extern struct device_attribute *zfcp_sysfs_shost_attrs[];
164 165
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index c66af27b230b..1e0eb089dfba 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -227,6 +227,8 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
227static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, 227static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
228 zfcp_sysfs_port_rescan_store); 228 zfcp_sysfs_port_rescan_store);
229 229
230DEFINE_MUTEX(zfcp_sysfs_port_units_mutex);
231
230static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, 232static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
231 struct device_attribute *attr, 233 struct device_attribute *attr,
232 const char *buf, size_t count) 234 const char *buf, size_t count)
@@ -249,6 +251,16 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
249 else 251 else
250 retval = 0; 252 retval = 0;
251 253
254 mutex_lock(&zfcp_sysfs_port_units_mutex);
255 if (atomic_read(&port->units) > 0) {
256 retval = -EBUSY;
257 mutex_unlock(&zfcp_sysfs_port_units_mutex);
258 goto out;
259 }
260 /* port is about to be removed, so no more unit_add */
261 atomic_set(&port->units, -1);
262 mutex_unlock(&zfcp_sysfs_port_units_mutex);
263
252 write_lock_irq(&adapter->port_list_lock); 264 write_lock_irq(&adapter->port_list_lock);
253 list_del(&port->list); 265 list_del(&port->list);
254 write_unlock_irq(&adapter->port_list_lock); 266 write_unlock_irq(&adapter->port_list_lock);
@@ -289,12 +301,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
289{ 301{
290 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); 302 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
291 u64 fcp_lun; 303 u64 fcp_lun;
304 int retval;
292 305
293 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) 306 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
294 return -EINVAL; 307 return -EINVAL;
295 308
296 if (zfcp_unit_add(port, fcp_lun)) 309 retval = zfcp_unit_add(port, fcp_lun);
297 return -EINVAL; 310 if (retval)
311 return retval;
298 312
299 return count; 313 return count;
300} 314}
diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c
index 3f2bff0d3aa2..1cd2b99ab256 100644
--- a/drivers/s390/scsi/zfcp_unit.c
+++ b/drivers/s390/scsi/zfcp_unit.c
@@ -104,7 +104,7 @@ static void zfcp_unit_release(struct device *dev)
104{ 104{
105 struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev); 105 struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
106 106
107 put_device(&unit->port->dev); 107 atomic_dec(&unit->port->units);
108 kfree(unit); 108 kfree(unit);
109} 109}
110 110
@@ -119,16 +119,27 @@ static void zfcp_unit_release(struct device *dev)
119int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) 119int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
120{ 120{
121 struct zfcp_unit *unit; 121 struct zfcp_unit *unit;
122 int retval = 0;
123
124 mutex_lock(&zfcp_sysfs_port_units_mutex);
125 if (atomic_read(&port->units) == -1) {
126 /* port is already gone */
127 retval = -ENODEV;
128 goto out;
129 }
122 130
123 unit = zfcp_unit_find(port, fcp_lun); 131 unit = zfcp_unit_find(port, fcp_lun);
124 if (unit) { 132 if (unit) {
125 put_device(&unit->dev); 133 put_device(&unit->dev);
126 return -EEXIST; 134 retval = -EEXIST;
135 goto out;
127 } 136 }
128 137
129 unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); 138 unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
130 if (!unit) 139 if (!unit) {
131 return -ENOMEM; 140 retval = -ENOMEM;
141 goto out;
142 }
132 143
133 unit->port = port; 144 unit->port = port;
134 unit->fcp_lun = fcp_lun; 145 unit->fcp_lun = fcp_lun;
@@ -139,28 +150,33 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
139 if (dev_set_name(&unit->dev, "0x%016llx", 150 if (dev_set_name(&unit->dev, "0x%016llx",
140 (unsigned long long) fcp_lun)) { 151 (unsigned long long) fcp_lun)) {
141 kfree(unit); 152 kfree(unit);
142 return -ENOMEM; 153 retval = -ENOMEM;
154 goto out;
143 } 155 }
144 156
145 get_device(&port->dev);
146
147 if (device_register(&unit->dev)) { 157 if (device_register(&unit->dev)) {
148 put_device(&unit->dev); 158 put_device(&unit->dev);
149 return -ENOMEM; 159 retval = -ENOMEM;
160 goto out;
150 } 161 }
151 162
152 if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) { 163 if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) {
153 device_unregister(&unit->dev); 164 device_unregister(&unit->dev);
154 return -EINVAL; 165 retval = -EINVAL;
166 goto out;
155 } 167 }
156 168
169 atomic_inc(&port->units); /* under zfcp_sysfs_port_units_mutex ! */
170
157 write_lock_irq(&port->unit_list_lock); 171 write_lock_irq(&port->unit_list_lock);
158 list_add_tail(&unit->list, &port->unit_list); 172 list_add_tail(&unit->list, &port->unit_list);
159 write_unlock_irq(&port->unit_list_lock); 173 write_unlock_irq(&port->unit_list_lock);
160 174
161 zfcp_unit_scsi_scan(unit); 175 zfcp_unit_scsi_scan(unit);
162 176
163 return 0; 177out:
178 mutex_unlock(&zfcp_sysfs_port_units_mutex);
179 return retval;
164} 180}
165 181
166/** 182/**