power: qpnp-bms: Fix memory leaks when probe fails
There exists a memory leak when the qpnp-bms driver's probe function
fails after loading the battery data. Fix this by freeing that memory
in the error handling of the probe function. Similarly, free the same
memory if the driver is removed dynamically.
Change-Id: I074605f8b698ab780c9f7e90feede261d082b05f
Signed-off-by: Xiaozhe Shi <xiaozhes@codeaurora.org>
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 61d4bf2..ad39e88 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -2687,7 +2687,8 @@
return;
}
- temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
+ temp = devm_kzalloc(chip->dev, sizeof(struct single_row_lut),
+ GFP_KERNEL);
if (!temp) {
pr_err("Cannot allocate memory for adjusted fcc table\n");
return;
@@ -2705,7 +2706,7 @@
old = chip->adjusted_fcc_temp_lut;
chip->adjusted_fcc_temp_lut = temp;
- kfree(old);
+ devm_kfree(chip->dev, old);
}
static int read_fcc_data_from_backup(struct qpnp_bms_chip *chip)
@@ -3442,7 +3443,7 @@
static int set_battery_data(struct qpnp_bms_chip *chip)
{
int64_t battery_id;
- int rc, dt_data = false;
+ int rc = 0, dt_data = false;
struct bms_battery_data *batt_data;
struct device_node *node;
@@ -3466,41 +3467,52 @@
node = of_find_node_by_name(chip->spmi->dev.of_node,
"qcom,battery-data");
- if (node) {
- batt_data = kzalloc(sizeof(struct bms_battery_data),
- GFP_KERNEL);
- batt_data->fcc_temp_lut = kzalloc(
- sizeof(struct single_row_lut),
- GFP_KERNEL);
- batt_data->pc_temp_ocv_lut = kzalloc(
- sizeof(struct pc_temp_ocv_lut),
- GFP_KERNEL);
- batt_data->rbatt_sf_lut = kzalloc(
- sizeof(struct sf_lut), GFP_KERNEL);
+ if (!node) {
+ pr_warn("No available batterydata, using palladium 1500\n");
+ batt_data = &palladium_1500_data;
+ goto assign_data;
+ }
+ batt_data = devm_kzalloc(chip->dev,
+ sizeof(struct bms_battery_data), GFP_KERNEL);
+ if (!batt_data) {
+ pr_err("Could not alloc battery data\n");
+ batt_data = &palladium_1500_data;
+ goto assign_data;
+ }
+ batt_data->fcc_temp_lut = devm_kzalloc(chip->dev,
+ sizeof(struct single_row_lut),
+ GFP_KERNEL);
+ batt_data->pc_temp_ocv_lut = devm_kzalloc(chip->dev,
+ sizeof(struct pc_temp_ocv_lut),
+ GFP_KERNEL);
+ batt_data->rbatt_sf_lut = devm_kzalloc(chip->dev,
+ sizeof(struct sf_lut),
+ GFP_KERNEL);
- batt_data->max_voltage_uv = -1;
- batt_data->cutoff_uv = -1;
- batt_data->iterm_ua = -1;
+ batt_data->max_voltage_uv = -1;
+ batt_data->cutoff_uv = -1;
+ batt_data->iterm_ua = -1;
- rc = of_batterydata_read_data(node,
- batt_data, battery_id);
- if (rc) {
- pr_err("battery data load failed, using palladium 1500\n");
- kfree(batt_data->fcc_temp_lut);
- kfree(batt_data->pc_temp_ocv_lut);
- kfree(batt_data->rbatt_sf_lut);
- kfree(batt_data);
- batt_data = &palladium_1500_data;
- } else {
- dt_data = true;
- }
+ /*
+ * if the alloced luts are 0s, of_batterydata_read_data ignores
+ * them.
+ */
+ rc = of_batterydata_read_data(node, batt_data, battery_id);
+ if (rc == 0 && batt_data->fcc_temp_lut
+ && batt_data->pc_temp_ocv_lut
+ && batt_data->rbatt_sf_lut) {
+ dt_data = true;
} else {
- pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
- battery_id);
+ pr_err("battery data load failed, using palladium 1500\n");
+ devm_kfree(chip->dev, batt_data->fcc_temp_lut);
+ devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut);
+ devm_kfree(chip->dev, batt_data->rbatt_sf_lut);
+ devm_kfree(chip->dev, batt_data);
batt_data = &palladium_1500_data;
}
}
+assign_data:
chip->fcc_mah = batt_data->fcc;
chip->fcc_temp_lut = batt_data->fcc_temp_lut;
chip->fcc_sf_lut = batt_data->fcc_sf_lut;
@@ -3519,13 +3531,20 @@
if (batt_data->iterm_ua >= 0 && dt_data)
chip->chg_term_ua = batt_data->iterm_ua;
- if (dt_data)
- kfree(batt_data);
-
if (chip->pc_temp_ocv_lut == NULL) {
- pr_err("temp ocv lut table is NULL\n");
+ pr_err("temp ocv lut table has not been loaded\n");
+ if (dt_data) {
+ devm_kfree(chip->dev, batt_data->fcc_temp_lut);
+ devm_kfree(chip->dev, batt_data->pc_temp_ocv_lut);
+ devm_kfree(chip->dev, batt_data->rbatt_sf_lut);
+ devm_kfree(chip->dev, batt_data);
+ }
return -EINVAL;
}
+
+ if (dt_data)
+ devm_kfree(chip->dev, batt_data);
+
return 0;
}
@@ -3642,6 +3661,8 @@
chip->fcc_learning_samples = devm_kzalloc(&chip->spmi->dev,
(sizeof(struct fcc_sample) *
chip->min_fcc_learning_samples), GFP_KERNEL);
+ if (chip->fcc_learning_samples == NULL)
+ return -ENOMEM;
pr_debug("min-fcc-soc=%d, min-fcc-pc=%d, min-fcc-cycles=%d\n",
chip->min_fcc_learning_soc, chip->min_fcc_ocv_pc,
chip->min_fcc_learning_samples);
@@ -3981,7 +4002,8 @@
bool warm_reset;
int rc, vbatt;
- chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ chip = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_bms_chip),
+ GFP_KERNEL);
if (chip == NULL) {
pr_err("kzalloc() failed.\n");
@@ -4172,17 +4194,12 @@
wake_lock_destroy(&chip->cv_wake_lock);
error_resource:
error_read:
- kfree(chip);
return rc;
}
-static int __devexit
-qpnp_bms_remove(struct spmi_device *spmi)
+static int qpnp_bms_remove(struct spmi_device *spmi)
{
- struct qpnp_bms_chip *chip = dev_get_drvdata(&spmi->dev);
-
dev_set_drvdata(&spmi->dev, NULL);
- kfree(chip);
return 0;
}