diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-11-17 20:59:47 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-19 14:37:47 -0500 |
commit | 735f7d2fedf57380214221be7bed7f62d729e262 (patch) | |
tree | 067db49c22dcbdf695a6517a6c8664b6bb2c2d32 /drivers/scsi/libsas/sas_discover.c | |
parent | 6f4e75a49fd07d707995865493b9f452302ae36b (diff) |
[SCSI] libsas: fix domain_device leak
Arrange for the deallocation of a struct domain_device object when it no
longer has:
1/ any children
2/ references by any scsi_targets
3/ references by a lldd
The comment about domain_device lifetime in
Documentation/scsi/libsas.txt is stale as it appears mainline never had
a version of a struct domain_device that was registered as a kobject.
We now manage domain_device reference counts on behalf of external
agents.
Reviewed-by: Jack Wang <jack_wang@usish.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libsas/sas_discover.c')
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 36 |
1 files changed, 24 insertions, 12 deletions
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 54a5199ceb56..4e649306ef4e 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c | |||
@@ -36,8 +36,6 @@ | |||
36 | 36 | ||
37 | void sas_init_dev(struct domain_device *dev) | 37 | void sas_init_dev(struct domain_device *dev) |
38 | { | 38 | { |
39 | INIT_LIST_HEAD(&dev->siblings); | ||
40 | INIT_LIST_HEAD(&dev->dev_list_node); | ||
41 | switch (dev->dev_type) { | 39 | switch (dev->dev_type) { |
42 | case SAS_END_DEV: | 40 | case SAS_END_DEV: |
43 | break; | 41 | break; |
@@ -73,14 +71,14 @@ static int sas_get_port_device(struct asd_sas_port *port) | |||
73 | struct sas_rphy *rphy; | 71 | struct sas_rphy *rphy; |
74 | struct domain_device *dev; | 72 | struct domain_device *dev; |
75 | 73 | ||
76 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 74 | dev = sas_alloc_device(); |
77 | if (!dev) | 75 | if (!dev) |
78 | return -ENOMEM; | 76 | return -ENOMEM; |
79 | 77 | ||
80 | spin_lock_irqsave(&port->phy_list_lock, flags); | 78 | spin_lock_irqsave(&port->phy_list_lock, flags); |
81 | if (list_empty(&port->phy_list)) { | 79 | if (list_empty(&port->phy_list)) { |
82 | spin_unlock_irqrestore(&port->phy_list_lock, flags); | 80 | spin_unlock_irqrestore(&port->phy_list_lock, flags); |
83 | kfree(dev); | 81 | sas_put_device(dev); |
84 | return -ENODEV; | 82 | return -ENODEV; |
85 | } | 83 | } |
86 | phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el); | 84 | phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el); |
@@ -130,7 +128,7 @@ static int sas_get_port_device(struct asd_sas_port *port) | |||
130 | } | 128 | } |
131 | 129 | ||
132 | if (!rphy) { | 130 | if (!rphy) { |
133 | kfree(dev); | 131 | sas_put_device(dev); |
134 | return -ENODEV; | 132 | return -ENODEV; |
135 | } | 133 | } |
136 | rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; | 134 | rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; |
@@ -173,6 +171,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) | |||
173 | dev_name(sas_ha->dev), | 171 | dev_name(sas_ha->dev), |
174 | SAS_ADDR(dev->sas_addr), res); | 172 | SAS_ADDR(dev->sas_addr), res); |
175 | } | 173 | } |
174 | kref_get(&dev->kref); | ||
176 | } | 175 | } |
177 | return res; | 176 | return res; |
178 | } | 177 | } |
@@ -184,8 +183,10 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) | |||
184 | struct Scsi_Host *shost = sas_ha->core.shost; | 183 | struct Scsi_Host *shost = sas_ha->core.shost; |
185 | struct sas_internal *i = to_sas_internal(shost->transportt); | 184 | struct sas_internal *i = to_sas_internal(shost->transportt); |
186 | 185 | ||
187 | if (i->dft->lldd_dev_gone) | 186 | if (i->dft->lldd_dev_gone) { |
188 | i->dft->lldd_dev_gone(dev); | 187 | i->dft->lldd_dev_gone(dev); |
188 | sas_put_device(dev); | ||
189 | } | ||
189 | } | 190 | } |
190 | 191 | ||
191 | /* ---------- Common/dispatchers ---------- */ | 192 | /* ---------- Common/dispatchers ---------- */ |
@@ -219,6 +220,20 @@ out_err2: | |||
219 | 220 | ||
220 | /* ---------- Device registration and unregistration ---------- */ | 221 | /* ---------- Device registration and unregistration ---------- */ |
221 | 222 | ||
223 | void sas_free_device(struct kref *kref) | ||
224 | { | ||
225 | struct domain_device *dev = container_of(kref, typeof(*dev), kref); | ||
226 | |||
227 | if (dev->parent) | ||
228 | sas_put_device(dev->parent); | ||
229 | |||
230 | /* remove the phys and ports, everything else should be gone */ | ||
231 | if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) | ||
232 | kfree(dev->ex_dev.ex_phy); | ||
233 | |||
234 | kfree(dev); | ||
235 | } | ||
236 | |||
222 | static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) | 237 | static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) |
223 | { | 238 | { |
224 | sas_notify_lldd_dev_gone(dev); | 239 | sas_notify_lldd_dev_gone(dev); |
@@ -230,6 +245,8 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d | |||
230 | spin_lock_irq(&port->dev_list_lock); | 245 | spin_lock_irq(&port->dev_list_lock); |
231 | list_del_init(&dev->dev_list_node); | 246 | list_del_init(&dev->dev_list_node); |
232 | spin_unlock_irq(&port->dev_list_lock); | 247 | spin_unlock_irq(&port->dev_list_lock); |
248 | |||
249 | sas_put_device(dev); | ||
233 | } | 250 | } |
234 | 251 | ||
235 | void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) | 252 | void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) |
@@ -239,11 +256,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) | |||
239 | sas_rphy_delete(dev->rphy); | 256 | sas_rphy_delete(dev->rphy); |
240 | dev->rphy = NULL; | 257 | dev->rphy = NULL; |
241 | } | 258 | } |
242 | if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) { | ||
243 | /* remove the phys and ports, everything else should be gone */ | ||
244 | kfree(dev->ex_dev.ex_phy); | ||
245 | dev->ex_dev.ex_phy = NULL; | ||
246 | } | ||
247 | sas_unregister_common_dev(port, dev); | 259 | sas_unregister_common_dev(port, dev); |
248 | } | 260 | } |
249 | 261 | ||
@@ -322,7 +334,7 @@ static void sas_discover_domain(struct work_struct *work) | |||
322 | list_del_init(&dev->dev_list_node); | 334 | list_del_init(&dev->dev_list_node); |
323 | spin_unlock_irq(&port->dev_list_lock); | 335 | spin_unlock_irq(&port->dev_list_lock); |
324 | 336 | ||
325 | kfree(dev); /* not kobject_register-ed yet */ | 337 | sas_put_device(dev); |
326 | port->port_dev = NULL; | 338 | port->port_dev = NULL; |
327 | } | 339 | } |
328 | 340 | ||