Skip to content

Commit 57b8ff0

Browse files
dtorgregkh
authored andcommitted
driver core: add devm_device_add_group() and friends
Many drivers create additional driver-specific device attributes when binding to the device, and providing managed version of device_create_group() will simplify unbinding and error handling in probe path for such drivers. Without managed version driver writers either have to mix manual and managed resources, which is prone to errors, or open-code this function by providing a wrapper to device_add_group() and use it with devm_add_action() or devm_add_action_or_reset(). Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent e323b2d commit 57b8ff0

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

drivers/base/core.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,136 @@ void device_remove_groups(struct device *dev,
10351035
}
10361036
EXPORT_SYMBOL_GPL(device_remove_groups);
10371037

1038+
union device_attr_group_devres {
1039+
const struct attribute_group *group;
1040+
const struct attribute_group **groups;
1041+
};
1042+
1043+
static int devm_attr_group_match(struct device *dev, void *res, void *data)
1044+
{
1045+
return ((union device_attr_group_devres *)res)->group == data;
1046+
}
1047+
1048+
static void devm_attr_group_remove(struct device *dev, void *res)
1049+
{
1050+
union device_attr_group_devres *devres = res;
1051+
const struct attribute_group *group = devres->group;
1052+
1053+
dev_dbg(dev, "%s: removing group %p\n", __func__, group);
1054+
sysfs_remove_group(&dev->kobj, group);
1055+
}
1056+
1057+
static void devm_attr_groups_remove(struct device *dev, void *res)
1058+
{
1059+
union device_attr_group_devres *devres = res;
1060+
const struct attribute_group **groups = devres->groups;
1061+
1062+
dev_dbg(dev, "%s: removing groups %p\n", __func__, groups);
1063+
sysfs_remove_groups(&dev->kobj, groups);
1064+
}
1065+
1066+
/**
1067+
* devm_device_add_group - given a device, create a managed attribute group
1068+
* @dev: The device to create the group for
1069+
* @grp: The attribute group to create
1070+
*
1071+
* This function creates a group for the first time. It will explicitly
1072+
* warn and error if any of the attribute files being created already exist.
1073+
*
1074+
* Returns 0 on success or error code on failure.
1075+
*/
1076+
int devm_device_add_group(struct device *dev, const struct attribute_group *grp)
1077+
{
1078+
union device_attr_group_devres *devres;
1079+
int error;
1080+
1081+
devres = devres_alloc(devm_attr_group_remove,
1082+
sizeof(*devres), GFP_KERNEL);
1083+
if (!devres)
1084+
return -ENOMEM;
1085+
1086+
error = sysfs_create_group(&dev->kobj, grp);
1087+
if (error) {
1088+
devres_free(devres);
1089+
return error;
1090+
}
1091+
1092+
devres->group = grp;
1093+
devres_add(dev, devres);
1094+
return 0;
1095+
}
1096+
EXPORT_SYMBOL_GPL(devm_device_add_group);
1097+
1098+
/**
1099+
* devm_device_remove_group: remove a managed group from a device
1100+
* @dev: device to remove the group from
1101+
* @grp: group to remove
1102+
*
1103+
* This function removes a group of attributes from a device. The attributes
1104+
* previously have to have been created for this group, otherwise it will fail.
1105+
*/
1106+
void devm_device_remove_group(struct device *dev,
1107+
const struct attribute_group *grp)
1108+
{
1109+
WARN_ON(devres_release(dev, devm_attr_group_remove,
1110+
devm_attr_group_match,
1111+
/* cast away const */ (void *)grp));
1112+
}
1113+
EXPORT_SYMBOL_GPL(devm_device_remove_group);
1114+
1115+
/**
1116+
* devm_device_add_groups - create a bunch of managed attribute groups
1117+
* @dev: The device to create the group for
1118+
* @groups: The attribute groups to create, NULL terminated
1119+
*
1120+
* This function creates a bunch of managed attribute groups. If an error
1121+
* occurs when creating a group, all previously created groups will be
1122+
* removed, unwinding everything back to the original state when this
1123+
* function was called. It will explicitly warn and error if any of the
1124+
* attribute files being created already exist.
1125+
*
1126+
* Returns 0 on success or error code from sysfs_create_group on failure.
1127+
*/
1128+
int devm_device_add_groups(struct device *dev,
1129+
const struct attribute_group **groups)
1130+
{
1131+
union device_attr_group_devres *devres;
1132+
int error;
1133+
1134+
devres = devres_alloc(devm_attr_groups_remove,
1135+
sizeof(*devres), GFP_KERNEL);
1136+
if (!devres)
1137+
return -ENOMEM;
1138+
1139+
error = sysfs_create_groups(&dev->kobj, groups);
1140+
if (error) {
1141+
devres_free(devres);
1142+
return error;
1143+
}
1144+
1145+
devres->groups = groups;
1146+
devres_add(dev, devres);
1147+
return 0;
1148+
}
1149+
EXPORT_SYMBOL_GPL(devm_device_add_groups);
1150+
1151+
/**
1152+
* devm_device_remove_groups - remove a list of managed groups
1153+
*
1154+
* @dev: The device for the groups to be removed from
1155+
* @groups: NULL terminated list of groups to be removed
1156+
*
1157+
* If groups is not NULL, remove the specified groups from the device.
1158+
*/
1159+
void devm_device_remove_groups(struct device *dev,
1160+
const struct attribute_group **groups)
1161+
{
1162+
WARN_ON(devres_release(dev, devm_attr_groups_remove,
1163+
devm_attr_group_match,
1164+
/* cast away const */ (void *)groups));
1165+
}
1166+
EXPORT_SYMBOL_GPL(devm_device_remove_groups);
1167+
10381168
static int device_add_attrs(struct device *dev)
10391169
{
10401170
struct class *class = dev->class;

include/linux/device.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,15 @@ static inline void device_remove_group(struct device *dev,
12211221
return device_remove_groups(dev, groups);
12221222
}
12231223

1224+
extern int __must_check devm_device_add_groups(struct device *dev,
1225+
const struct attribute_group **groups);
1226+
extern void devm_device_remove_groups(struct device *dev,
1227+
const struct attribute_group **groups);
1228+
extern int __must_check devm_device_add_group(struct device *dev,
1229+
const struct attribute_group *grp);
1230+
extern void devm_device_remove_group(struct device *dev,
1231+
const struct attribute_group *grp);
1232+
12241233
/*
12251234
* Platform "fixup" functions - allow the platform to have their say
12261235
* about devices and actions that the general device layer doesn't

0 commit comments

Comments
 (0)