aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power/bq2415x_charger.c
diff options
context:
space:
mode:
authorKrzysztof Kozlowski <k.kozlowski@samsung.com>2015-03-12 03:44:11 -0400
committerSebastian Reichel <sre@kernel.org>2015-03-13 18:15:51 -0400
commit297d716f6260cc9421d971b124ca196b957ee458 (patch)
tree32a666d3374d7f0653258c766252bd6a841f05ab /drivers/power/bq2415x_charger.c
parentb70229bca127283c3d30e5f471d30b1acccd7096 (diff)
power_supply: Change ownership from driver to core
Change the ownership of power_supply structure from each driver implementing the class to the power supply core. The patch changes power_supply_register() function thus all drivers implementing power supply class are adjusted. Each driver provides the implementation of power supply. However it should not be the owner of power supply class instance because it is exposed by core to other subsystems with power_supply_get_by_name(). These other subsystems have no knowledge when the driver will unregister the power supply. This leads to several issues when driver is unbound - mostly because user of power supply accesses freed memory. Instead let the core own the instance of struct 'power_supply'. Other users of this power supply will still access valid memory because it will be freed when device reference count reaches 0. Currently this means "it will leak" but power_supply_put() call in next patches will solve it. This solves invalid memory references in following race condition scenario: Thread 1: charger manager Thread 2: power supply driver, used by charger manager THREAD 1 (charger manager) THREAD 2 (power supply driver) ========================== ============================== psy = power_supply_get_by_name() Driver unbind, .remove power_supply_unregister() Device fully removed psy->get_property() The 'get_property' call is executed in invalid context because the driver was unbound and struct 'power_supply' memory was freed. This could be observed easily with charger manager driver (here compiled with max17040 fuel gauge): $ cat /sys/devices/virtual/power_supply/cm-battery/capacity & $ echo "1-0036" > /sys/bus/i2c/drivers/max17040/unbind [ 55.725123] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 55.732584] pgd = d98d4000 [ 55.734060] [00000000] *pgd=5afa2831, *pte=00000000, *ppte=00000000 [ 55.740318] Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM [ 55.746210] Modules linked in: [ 55.749259] CPU: 1 PID: 2936 Comm: cat Tainted: G W 3.19.0-rc1-next-20141226-00048-gf79f475f3c44-dirty #1496 [ 55.760190] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 55.766270] task: d9b76f00 ti: daf54000 task.ti: daf54000 [ 55.771647] PC is at 0x0 [ 55.774182] LR is at charger_get_property+0x2f4/0x36c [ 55.779201] pc : [<00000000>] lr : [<c034b0b4>] psr: 60000013 [ 55.779201] sp : daf55e90 ip : 00000003 fp : 00000000 [ 55.790657] r10: 00000000 r9 : c06e2878 r8 : d9b26c68 [ 55.795865] r7 : dad81610 r6 : daec7410 r5 : daf55ebc r4 : 00000000 [ 55.802367] r3 : 00000000 r2 : daf55ebc r1 : 0000002a r0 : d9b26c68 [ 55.808879] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 55.815994] Control: 10c5387d Table: 598d406a DAC: 00000015 [ 55.821723] Process cat (pid: 2936, stack limit = 0xdaf54210) [ 55.827451] Stack: (0xdaf55e90 to 0xdaf56000) [ 55.831795] 5e80: 60000013 c01459c4 0000002a c06f8ef8 [ 55.839956] 5ea0: db651000 c06f8ef8 daebac00 c04cb668 daebac08 c0346864 00000000 c01459c4 [ 55.848115] 5ec0: d99eaa80 c06f8ef8 00000fff 00001000 db651000 c027f25c c027f240 d99eaa80 [ 55.856274] 5ee0: d9a06c00 c0146218 daf55f18 00001000 d99eaa80 db4c18c0 00000001 00000001 [ 55.864468] 5f00: daf55f80 c0144c78 c0144c54 c0107f90 00015000 d99eaab0 00000000 00000000 [ 55.872603] 5f20: 000051c7 00000000 db4c18c0 c04a9370 00015000 00001000 daf55f80 00001000 [ 55.880763] 5f40: daf54000 00015000 00000000 c00e53dc db4c18c0 c00e548c 0000000d 00008124 [ 55.888937] 5f60: 00000001 00000000 00000000 db4c18c0 db4c18c0 00001000 00015000 c00e5550 [ 55.897099] 5f80: 00000000 00000000 00001000 00001000 00015000 00000003 00000003 c000f364 [ 55.905239] 5fa0: 00000000 c000f1a0 00001000 00015000 00000003 00015000 00001000 0001333c [ 55.913399] 5fc0: 00001000 00015000 00000003 00000003 00000002 00000000 00000000 00000000 [ 55.921560] 5fe0: 7fffe000 be999850 0000a225 b6f3c19c 60000010 00000003 00000000 00000000 [ 55.929744] [<c034b0b4>] (charger_get_property) from [<c0346864>] (power_supply_show_property+0x48/0x20c) [ 55.939286] [<c0346864>] (power_supply_show_property) from [<c027f25c>] (dev_attr_show+0x1c/0x48) [ 55.948130] [<c027f25c>] (dev_attr_show) from [<c0146218>] (sysfs_kf_seq_show+0x84/0x104) [ 55.956298] [<c0146218>] (sysfs_kf_seq_show) from [<c0144c78>] (kernfs_seq_show+0x24/0x28) [ 55.964536] [<c0144c78>] (kernfs_seq_show) from [<c0107f90>] (seq_read+0x1b0/0x484) [ 55.972172] [<c0107f90>] (seq_read) from [<c00e53dc>] (__vfs_read+0x18/0x4c) [ 55.979188] [<c00e53dc>] (__vfs_read) from [<c00e548c>] (vfs_read+0x7c/0x100) [ 55.986304] [<c00e548c>] (vfs_read) from [<c00e5550>] (SyS_read+0x40/0x8c) [ 55.993164] [<c00e5550>] (SyS_read) from [<c000f1a0>] (ret_fast_syscall+0x0/0x48) [ 56.000626] Code: bad PC value [ 56.011652] ---[ end trace 7b64343fbdae8ef1 ]--- Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> [for the nvec part] Reviewed-by: Marc Dietrich <marvin24@gmx.de> [for compal-laptop.c] Acked-by: Darren Hart <dvhart@linux.intel.com> [for the mfd part] Acked-by: Lee Jones <lee.jones@linaro.org> [for the hid part] Acked-by: Jiri Kosina <jkosina@suse.cz> [for the acpi part] Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Sebastian Reichel <sre@kernel.org>
Diffstat (limited to 'drivers/power/bq2415x_charger.c')
-rw-r--r--drivers/power/bq2415x_charger.c73
1 files changed, 32 insertions, 41 deletions
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
index d31ccc7935d0..c745d278815d 100644
--- a/drivers/power/bq2415x_charger.c
+++ b/drivers/power/bq2415x_charger.c
@@ -166,7 +166,8 @@ static char *bq2415x_chip_name[] = {
166struct bq2415x_device { 166struct bq2415x_device {
167 struct device *dev; 167 struct device *dev;
168 struct bq2415x_platform_data init_data; 168 struct bq2415x_platform_data init_data;
169 struct power_supply charger; 169 struct power_supply *charger;
170 struct power_supply_desc charger_desc;
170 struct delayed_work work; 171 struct delayed_work work;
171 struct power_supply *notify_psy; 172 struct power_supply *notify_psy;
172 struct notifier_block nb; 173 struct notifier_block nb;
@@ -784,7 +785,7 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
784 bq2415x_set_default_value(bq, battery_regulation_voltage); 785 bq2415x_set_default_value(bq, battery_regulation_voltage);
785 786
786 bq->mode = mode; 787 bq->mode = mode;
787 sysfs_notify(&bq->charger.dev->kobj, NULL, "mode"); 788 sysfs_notify(&bq->charger->dev.kobj, NULL, "mode");
788 789
789 return 0; 790 return 0;
790 791
@@ -868,7 +869,7 @@ static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state)
868static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) 869static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
869{ 870{
870 bq->timer_error = msg; 871 bq->timer_error = msg;
871 sysfs_notify(&bq->charger.dev->kobj, NULL, "timer"); 872 sysfs_notify(&bq->charger->dev.kobj, NULL, "timer");
872 dev_err(bq->dev, "%s\n", msg); 873 dev_err(bq->dev, "%s\n", msg);
873 if (bq->automode > 0) 874 if (bq->automode > 0)
874 bq->automode = 0; 875 bq->automode = 0;
@@ -886,7 +887,7 @@ static void bq2415x_timer_work(struct work_struct *work)
886 int boost; 887 int boost;
887 888
888 if (bq->automode > 0 && (bq->reported_mode != bq->mode)) { 889 if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
889 sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode"); 890 sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode");
890 bq2415x_set_mode(bq, bq->reported_mode); 891 bq2415x_set_mode(bq, bq->reported_mode);
891 } 892 }
892 893
@@ -992,8 +993,7 @@ static int bq2415x_power_supply_get_property(struct power_supply *psy,
992 enum power_supply_property psp, 993 enum power_supply_property psp,
993 union power_supply_propval *val) 994 union power_supply_propval *val)
994{ 995{
995 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 996 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
996 charger);
997 int ret; 997 int ret;
998 998
999 switch (psp) { 999 switch (psp) {
@@ -1024,12 +1024,14 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
1024 int ret; 1024 int ret;
1025 int chip; 1025 int chip;
1026 char revstr[8]; 1026 char revstr[8];
1027 struct power_supply_config psy_cfg = { .drv_data = bq, };
1027 1028
1028 bq->charger.name = bq->name; 1029 bq->charger_desc.name = bq->name;
1029 bq->charger.type = POWER_SUPPLY_TYPE_USB; 1030 bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
1030 bq->charger.properties = bq2415x_power_supply_props; 1031 bq->charger_desc.properties = bq2415x_power_supply_props;
1031 bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props); 1032 bq->charger_desc.num_properties =
1032 bq->charger.get_property = bq2415x_power_supply_get_property; 1033 ARRAY_SIZE(bq2415x_power_supply_props);
1034 bq->charger_desc.get_property = bq2415x_power_supply_get_property;
1033 1035
1034 ret = bq2415x_detect_chip(bq); 1036 ret = bq2415x_detect_chip(bq);
1035 if (ret < 0) 1037 if (ret < 0)
@@ -1052,10 +1054,11 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
1052 return -ENOMEM; 1054 return -ENOMEM;
1053 } 1055 }
1054 1056
1055 ret = power_supply_register(bq->dev, &bq->charger, NULL); 1057 bq->charger = power_supply_register(bq->dev, &bq->charger_desc,
1056 if (ret) { 1058 &psy_cfg);
1059 if (IS_ERR(bq->charger)) {
1057 kfree(bq->model); 1060 kfree(bq->model);
1058 return ret; 1061 return PTR_ERR(bq->charger);
1059 } 1062 }
1060 1063
1061 return 0; 1064 return 0;
@@ -1067,7 +1070,7 @@ static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
1067 if (bq->automode > 0) 1070 if (bq->automode > 0)
1068 bq->automode = 0; 1071 bq->automode = 0;
1069 cancel_delayed_work_sync(&bq->work); 1072 cancel_delayed_work_sync(&bq->work);
1070 power_supply_unregister(&bq->charger); 1073 power_supply_unregister(bq->charger);
1071 kfree(bq->model); 1074 kfree(bq->model);
1072} 1075}
1073 1076
@@ -1079,8 +1082,7 @@ static ssize_t bq2415x_sysfs_show_status(struct device *dev,
1079 char *buf) 1082 char *buf)
1080{ 1083{
1081 struct power_supply *psy = dev_get_drvdata(dev); 1084 struct power_supply *psy = dev_get_drvdata(dev);
1082 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1085 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1083 charger);
1084 enum bq2415x_command command; 1086 enum bq2415x_command command;
1085 int ret; 1087 int ret;
1086 1088
@@ -1113,8 +1115,7 @@ static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
1113 size_t count) 1115 size_t count)
1114{ 1116{
1115 struct power_supply *psy = dev_get_drvdata(dev); 1117 struct power_supply *psy = dev_get_drvdata(dev);
1116 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1118 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1117 charger);
1118 int ret = 0; 1119 int ret = 0;
1119 1120
1120 if (strncmp(buf, "auto", 4) == 0) 1121 if (strncmp(buf, "auto", 4) == 0)
@@ -1135,8 +1136,7 @@ static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
1135 char *buf) 1136 char *buf)
1136{ 1137{
1137 struct power_supply *psy = dev_get_drvdata(dev); 1138 struct power_supply *psy = dev_get_drvdata(dev);
1138 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1139 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1139 charger);
1140 1140
1141 if (bq->timer_error) 1141 if (bq->timer_error)
1142 return sprintf(buf, "%s\n", bq->timer_error); 1142 return sprintf(buf, "%s\n", bq->timer_error);
@@ -1160,8 +1160,7 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
1160 size_t count) 1160 size_t count)
1161{ 1161{
1162 struct power_supply *psy = dev_get_drvdata(dev); 1162 struct power_supply *psy = dev_get_drvdata(dev);
1163 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1163 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1164 charger);
1165 enum bq2415x_mode mode; 1164 enum bq2415x_mode mode;
1166 int ret = 0; 1165 int ret = 0;
1167 1166
@@ -1213,8 +1212,7 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
1213 char *buf) 1212 char *buf)
1214{ 1213{
1215 struct power_supply *psy = dev_get_drvdata(dev); 1214 struct power_supply *psy = dev_get_drvdata(dev);
1216 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1215 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1217 charger);
1218 ssize_t ret = 0; 1216 ssize_t ret = 0;
1219 1217
1220 if (bq->automode > 0) 1218 if (bq->automode > 0)
@@ -1251,8 +1249,7 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
1251 char *buf) 1249 char *buf)
1252{ 1250{
1253 struct power_supply *psy = dev_get_drvdata(dev); 1251 struct power_supply *psy = dev_get_drvdata(dev);
1254 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1252 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1255 charger);
1256 1253
1257 if (bq->automode < 0) 1254 if (bq->automode < 0)
1258 return -EINVAL; 1255 return -EINVAL;
@@ -1280,8 +1277,7 @@ static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
1280 size_t count) 1277 size_t count)
1281{ 1278{
1282 struct power_supply *psy = dev_get_drvdata(dev); 1279 struct power_supply *psy = dev_get_drvdata(dev);
1283 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1280 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1284 charger);
1285 ssize_t ret = 0; 1281 ssize_t ret = 0;
1286 unsigned int reg; 1282 unsigned int reg;
1287 unsigned int val; 1283 unsigned int val;
@@ -1316,8 +1312,7 @@ static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
1316 char *buf) 1312 char *buf)
1317{ 1313{
1318 struct power_supply *psy = dev_get_drvdata(dev); 1314 struct power_supply *psy = dev_get_drvdata(dev);
1319 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1315 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1320 charger);
1321 ssize_t ret = 0; 1316 ssize_t ret = 0;
1322 1317
1323 ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret); 1318 ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret);
@@ -1335,8 +1330,7 @@ static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
1335 size_t count) 1330 size_t count)
1336{ 1331{
1337 struct power_supply *psy = dev_get_drvdata(dev); 1332 struct power_supply *psy = dev_get_drvdata(dev);
1338 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1333 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1339 charger);
1340 long val; 1334 long val;
1341 int ret; 1335 int ret;
1342 1336
@@ -1367,8 +1361,7 @@ static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
1367 char *buf) 1361 char *buf)
1368{ 1362{
1369 struct power_supply *psy = dev_get_drvdata(dev); 1363 struct power_supply *psy = dev_get_drvdata(dev);
1370 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1364 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1371 charger);
1372 int ret; 1365 int ret;
1373 1366
1374 if (strcmp(attr->attr.name, "current_limit") == 0) 1367 if (strcmp(attr->attr.name, "current_limit") == 0)
@@ -1396,8 +1389,7 @@ static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
1396 size_t count) 1389 size_t count)
1397{ 1390{
1398 struct power_supply *psy = dev_get_drvdata(dev); 1391 struct power_supply *psy = dev_get_drvdata(dev);
1399 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1392 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1400 charger);
1401 enum bq2415x_command command; 1393 enum bq2415x_command command;
1402 long val; 1394 long val;
1403 int ret; 1395 int ret;
@@ -1432,8 +1424,7 @@ static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
1432 char *buf) 1424 char *buf)
1433{ 1425{
1434 struct power_supply *psy = dev_get_drvdata(dev); 1426 struct power_supply *psy = dev_get_drvdata(dev);
1435 struct bq2415x_device *bq = container_of(psy, struct bq2415x_device, 1427 struct bq2415x_device *bq = power_supply_get_drvdata(psy);
1436 charger);
1437 enum bq2415x_command command; 1428 enum bq2415x_command command;
1438 int ret; 1429 int ret;
1439 1430
@@ -1524,13 +1515,13 @@ static const struct attribute_group bq2415x_sysfs_attr_group = {
1524 1515
1525static int bq2415x_sysfs_init(struct bq2415x_device *bq) 1516static int bq2415x_sysfs_init(struct bq2415x_device *bq)
1526{ 1517{
1527 return sysfs_create_group(&bq->charger.dev->kobj, 1518 return sysfs_create_group(&bq->charger->dev.kobj,
1528 &bq2415x_sysfs_attr_group); 1519 &bq2415x_sysfs_attr_group);
1529} 1520}
1530 1521
1531static void bq2415x_sysfs_exit(struct bq2415x_device *bq) 1522static void bq2415x_sysfs_exit(struct bq2415x_device *bq)
1532{ 1523{
1533 sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group); 1524 sysfs_remove_group(&bq->charger->dev.kobj, &bq2415x_sysfs_attr_group);
1534} 1525}
1535 1526
1536/* main bq2415x probe function */ 1527/* main bq2415x probe function */