Skip to content

Commit 3ebed2f

Browse files
Wer-Wolfij-intel
authored andcommitted
power: supply: core: Add power_supply_get/set_property_direct()
Power supply extensions might want to interact with the underlying power supply to retrieve data like serial numbers, charging status and more. However doing so causes psy->extensions_sem to be locked twice, possibly causing a deadlock. Provide special variants of power_supply_get/set_property() that ignore any power supply extensions and thus do not touch the associated psy->extensions_sem lock. Suggested-by: Hans de Goede <[email protected]> Signed-off-by: Armin Wolf <[email protected]> Acked-by: Sebastian Reichel <[email protected]> Reviewed-by: Hans de Goede <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Ilpo Järvinen <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]>
1 parent 8346c6a commit 3ebed2f

2 files changed

Lines changed: 78 additions & 12 deletions

File tree

drivers/power/supply/power_supply_core.c

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,9 +1235,8 @@ bool power_supply_has_property(struct power_supply *psy,
12351235
return false;
12361236
}
12371237

1238-
int power_supply_get_property(struct power_supply *psy,
1239-
enum power_supply_property psp,
1240-
union power_supply_propval *val)
1238+
static int __power_supply_get_property(struct power_supply *psy, enum power_supply_property psp,
1239+
union power_supply_propval *val, bool use_extensions)
12411240
{
12421241
struct power_supply_ext_registration *reg;
12431242

@@ -1247,10 +1246,14 @@ int power_supply_get_property(struct power_supply *psy,
12471246
return -ENODEV;
12481247
}
12491248

1250-
scoped_guard(rwsem_read, &psy->extensions_sem) {
1251-
power_supply_for_each_extension(reg, psy) {
1252-
if (power_supply_ext_has_property(reg->ext, psp))
1249+
if (use_extensions) {
1250+
scoped_guard(rwsem_read, &psy->extensions_sem) {
1251+
power_supply_for_each_extension(reg, psy) {
1252+
if (!power_supply_ext_has_property(reg->ext, psp))
1253+
continue;
1254+
12531255
return reg->ext->get_property(psy, reg->ext, reg->data, psp, val);
1256+
}
12541257
}
12551258
}
12561259

@@ -1261,20 +1264,49 @@ int power_supply_get_property(struct power_supply *psy,
12611264
else
12621265
return -EINVAL;
12631266
}
1267+
1268+
int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp,
1269+
union power_supply_propval *val)
1270+
{
1271+
return __power_supply_get_property(psy, psp, val, true);
1272+
}
12641273
EXPORT_SYMBOL_GPL(power_supply_get_property);
12651274

1266-
int power_supply_set_property(struct power_supply *psy,
1267-
enum power_supply_property psp,
1268-
const union power_supply_propval *val)
1275+
/**
1276+
* power_supply_get_property_direct - Read a power supply property without checking for extensions
1277+
* @psy: The power supply
1278+
* @psp: The power supply property to read
1279+
* @val: The resulting value of the power supply property
1280+
*
1281+
* Read a power supply property without taking into account any power supply extensions registered
1282+
* on the given power supply. This is mostly useful for power supply extensions that want to access
1283+
* their own power supply as using power_supply_get_property() directly will result in a potential
1284+
* deadlock.
1285+
*
1286+
* Return: 0 on success or negative error code on failure.
1287+
*/
1288+
int power_supply_get_property_direct(struct power_supply *psy, enum power_supply_property psp,
1289+
union power_supply_propval *val)
1290+
{
1291+
return __power_supply_get_property(psy, psp, val, false);
1292+
}
1293+
EXPORT_SYMBOL_GPL(power_supply_get_property_direct);
1294+
1295+
1296+
static int __power_supply_set_property(struct power_supply *psy, enum power_supply_property psp,
1297+
const union power_supply_propval *val, bool use_extensions)
12691298
{
12701299
struct power_supply_ext_registration *reg;
12711300

12721301
if (atomic_read(&psy->use_cnt) <= 0)
12731302
return -ENODEV;
12741303

1275-
scoped_guard(rwsem_read, &psy->extensions_sem) {
1276-
power_supply_for_each_extension(reg, psy) {
1277-
if (power_supply_ext_has_property(reg->ext, psp)) {
1304+
if (use_extensions) {
1305+
scoped_guard(rwsem_read, &psy->extensions_sem) {
1306+
power_supply_for_each_extension(reg, psy) {
1307+
if (!power_supply_ext_has_property(reg->ext, psp))
1308+
continue;
1309+
12781310
if (reg->ext->set_property)
12791311
return reg->ext->set_property(psy, reg->ext, reg->data,
12801312
psp, val);
@@ -1289,8 +1321,34 @@ int power_supply_set_property(struct power_supply *psy,
12891321

12901322
return psy->desc->set_property(psy, psp, val);
12911323
}
1324+
1325+
int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp,
1326+
const union power_supply_propval *val)
1327+
{
1328+
return __power_supply_set_property(psy, psp, val, true);
1329+
}
12921330
EXPORT_SYMBOL_GPL(power_supply_set_property);
12931331

1332+
/**
1333+
* power_supply_set_property_direct - Write a power supply property without checking for extensions
1334+
* @psy: The power supply
1335+
* @psp: The power supply property to write
1336+
* @val: The value to write to the power supply property
1337+
*
1338+
* Write a power supply property without taking into account any power supply extensions registered
1339+
* on the given power supply. This is mostly useful for power supply extensions that want to access
1340+
* their own power supply as using power_supply_set_property() directly will result in a potential
1341+
* deadlock.
1342+
*
1343+
* Return: 0 on success or negative error code on failure.
1344+
*/
1345+
int power_supply_set_property_direct(struct power_supply *psy, enum power_supply_property psp,
1346+
const union power_supply_propval *val)
1347+
{
1348+
return __power_supply_set_property(psy, psp, val, false);
1349+
}
1350+
EXPORT_SYMBOL_GPL(power_supply_set_property_direct);
1351+
12941352
int power_supply_property_is_writeable(struct power_supply *psy,
12951353
enum power_supply_property psp)
12961354
{

include/linux/power_supply.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,15 +888,23 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
888888
extern int power_supply_get_property(struct power_supply *psy,
889889
enum power_supply_property psp,
890890
union power_supply_propval *val);
891+
int power_supply_get_property_direct(struct power_supply *psy, enum power_supply_property psp,
892+
union power_supply_propval *val);
891893
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
892894
extern int power_supply_set_property(struct power_supply *psy,
893895
enum power_supply_property psp,
894896
const union power_supply_propval *val);
897+
int power_supply_set_property_direct(struct power_supply *psy, enum power_supply_property psp,
898+
const union power_supply_propval *val);
895899
#else
896900
static inline int power_supply_set_property(struct power_supply *psy,
897901
enum power_supply_property psp,
898902
const union power_supply_propval *val)
899903
{ return 0; }
904+
static inline int power_supply_set_property_direct(struct power_supply *psy,
905+
enum power_supply_property psp,
906+
const union power_supply_propval *val)
907+
{ return 0; }
900908
#endif
901909
extern void power_supply_external_power_changed(struct power_supply *psy);
902910

0 commit comments

Comments
 (0)