aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSebastian Sanchez <sebastian.sanchez@intel.com>2018-05-01 08:36:06 -0400
committerDoug Ledford <dledford@redhat.com>2018-05-03 15:24:48 -0400
commite9777ad4399c26c70318c4945f94efac2ed95391 (patch)
treefd73bf21fcef7c7c9321aa3041141fb822080af2 /drivers
parent45d924571a5e1329580811f2419da61b07ac3613 (diff)
IB/{hfi1, rdmavt}: Fix memory leak in hfi1_alloc_devdata() upon failure
When allocating device data, if there's an allocation failure, the already allocated memory won't be freed such as per-cpu counters. Fix memory leaks in exception path by creating a common reentrant clean up function hfi1_clean_devdata() to be used at driver unload time and device data allocation failure. To accomplish this, free_platform_config() and clean_up_i2c() are changed to be reentrant to remove dependencies when they are called in different order. This helps avoid NULL pointer dereferences introduced by this patch if those two functions weren't reentrant. In addition, set dd->int_counter, dd->rcv_limit, dd->send_schedule and dd->tx_opstats to NULL after they're freed in hfi1_clean_devdata(), so that hfi1_clean_devdata() is fully reentrant. Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com> Reviewed-by: Michael J. Ruhl <michael.j.ruhl@intel.com> Signed-off-by: Sebastian Sanchez <sebastian.sanchez@intel.com> Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/infiniband/hw/hfi1/init.c37
-rw-r--r--drivers/infiniband/hw/hfi1/platform.c1
-rw-r--r--drivers/infiniband/hw/hfi1/qsfp.c2
3 files changed, 30 insertions, 10 deletions
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index b417e3b40e4a..6309edf811df 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -1209,30 +1209,49 @@ static void finalize_asic_data(struct hfi1_devdata *dd,
1209 kfree(ad); 1209 kfree(ad);
1210} 1210}
1211 1211
1212static void __hfi1_free_devdata(struct kobject *kobj) 1212/**
1213 * hfi1_clean_devdata - cleans up per-unit data structure
1214 * @dd: pointer to a valid devdata structure
1215 *
1216 * It cleans up all data structures set up by
1217 * by hfi1_alloc_devdata().
1218 */
1219static void hfi1_clean_devdata(struct hfi1_devdata *dd)
1213{ 1220{
1214 struct hfi1_devdata *dd =
1215 container_of(kobj, struct hfi1_devdata, kobj);
1216 struct hfi1_asic_data *ad; 1221 struct hfi1_asic_data *ad;
1217 unsigned long flags; 1222 unsigned long flags;
1218 1223
1219 spin_lock_irqsave(&hfi1_devs_lock, flags); 1224 spin_lock_irqsave(&hfi1_devs_lock, flags);
1220 idr_remove(&hfi1_unit_table, dd->unit); 1225 if (!list_empty(&dd->list)) {
1221 list_del(&dd->list); 1226 idr_remove(&hfi1_unit_table, dd->unit);
1227 list_del_init(&dd->list);
1228 }
1222 ad = release_asic_data(dd); 1229 ad = release_asic_data(dd);
1223 spin_unlock_irqrestore(&hfi1_devs_lock, flags); 1230 spin_unlock_irqrestore(&hfi1_devs_lock, flags);
1224 if (ad) 1231
1225 finalize_asic_data(dd, ad); 1232 finalize_asic_data(dd, ad);
1226 free_platform_config(dd); 1233 free_platform_config(dd);
1227 rcu_barrier(); /* wait for rcu callbacks to complete */ 1234 rcu_barrier(); /* wait for rcu callbacks to complete */
1228 free_percpu(dd->int_counter); 1235 free_percpu(dd->int_counter);
1229 free_percpu(dd->rcv_limit); 1236 free_percpu(dd->rcv_limit);
1230 free_percpu(dd->send_schedule); 1237 free_percpu(dd->send_schedule);
1231 free_percpu(dd->tx_opstats); 1238 free_percpu(dd->tx_opstats);
1239 dd->int_counter = NULL;
1240 dd->rcv_limit = NULL;
1241 dd->send_schedule = NULL;
1242 dd->tx_opstats = NULL;
1232 sdma_clean(dd, dd->num_sdma); 1243 sdma_clean(dd, dd->num_sdma);
1233 rvt_dealloc_device(&dd->verbs_dev.rdi); 1244 rvt_dealloc_device(&dd->verbs_dev.rdi);
1234} 1245}
1235 1246
1247static void __hfi1_free_devdata(struct kobject *kobj)
1248{
1249 struct hfi1_devdata *dd =
1250 container_of(kobj, struct hfi1_devdata, kobj);
1251
1252 hfi1_clean_devdata(dd);
1253}
1254
1236static struct kobj_type hfi1_devdata_type = { 1255static struct kobj_type hfi1_devdata_type = {
1237 .release = __hfi1_free_devdata, 1256 .release = __hfi1_free_devdata,
1238}; 1257};
@@ -1333,9 +1352,7 @@ struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, size_t extra)
1333 return dd; 1352 return dd;
1334 1353
1335bail: 1354bail:
1336 if (!list_empty(&dd->list)) 1355 hfi1_clean_devdata(dd);
1337 list_del_init(&dd->list);
1338 rvt_dealloc_device(&dd->verbs_dev.rdi);
1339 return ERR_PTR(ret); 1356 return ERR_PTR(ret);
1340} 1357}
1341 1358
diff --git a/drivers/infiniband/hw/hfi1/platform.c b/drivers/infiniband/hw/hfi1/platform.c
index d486355880cb..cbf7faa5038c 100644
--- a/drivers/infiniband/hw/hfi1/platform.c
+++ b/drivers/infiniband/hw/hfi1/platform.c
@@ -199,6 +199,7 @@ void free_platform_config(struct hfi1_devdata *dd)
199{ 199{
200 /* Release memory allocated for eprom or fallback file read. */ 200 /* Release memory allocated for eprom or fallback file read. */
201 kfree(dd->platform_config.data); 201 kfree(dd->platform_config.data);
202 dd->platform_config.data = NULL;
202} 203}
203 204
204void get_port_type(struct hfi1_pportdata *ppd) 205void get_port_type(struct hfi1_pportdata *ppd)
diff --git a/drivers/infiniband/hw/hfi1/qsfp.c b/drivers/infiniband/hw/hfi1/qsfp.c
index 1869f639c3ae..b5966991d647 100644
--- a/drivers/infiniband/hw/hfi1/qsfp.c
+++ b/drivers/infiniband/hw/hfi1/qsfp.c
@@ -204,6 +204,8 @@ static void clean_i2c_bus(struct hfi1_i2c_bus *bus)
204 204
205void clean_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad) 205void clean_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad)
206{ 206{
207 if (!ad)
208 return;
207 clean_i2c_bus(ad->i2c_bus0); 209 clean_i2c_bus(ad->i2c_bus0);
208 ad->i2c_bus0 = NULL; 210 ad->i2c_bus0 = NULL;
209 clean_i2c_bus(ad->i2c_bus1); 211 clean_i2c_bus(ad->i2c_bus1);