mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
UPSTREAM: driver core: Expose device link details in sysfs
It's helpful to be able to look at device link details from sysfs. So, expose it in sysfs. Say device-A is supplier of device-B. These are the additional files this patch would create: /sys/class/devlink/device-A:device-B/ auto_remove_on consumer/ -> .../device-B/ runtime_pm status supplier/ -> .../device-A/ sync_state_only /sys/devices/.../device-A/ consumer:device-B/ -> /sys/class/devlink/device-A:device-B/ /sys/devices/.../device-B/ supplier:device-A/ -> /sys/class/devlink/device-A:device-B/ That way: To get a list of all the device link in the system: ls /sys/class/devlink/ To get the consumer names and links of a device: ls -d /sys/devices/.../device-X/consumer:* To get the supplier names and links of a device: ls -d /sys/devices/.../device-X/supplier:* Signed-off-by: Saravana Kannan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit 287905e68dd29873bcb7986a8290cd1e4cfde600) Signed-off-by: Saravana Kannan <[email protected]> Bug: 163090256 Change-Id: I41dafdd731b8d429b5a21fd2e8d4d396b2bb571e
This commit is contained in:
parent
c88a69f0e9
commit
3b499928c2
5 changed files with 375 additions and 36 deletions
|
|
@ -235,6 +235,186 @@ void device_pm_move_to_tail(struct device *dev)
|
|||
device_links_read_unlock(idx);
|
||||
}
|
||||
|
||||
#define to_devlink(dev) container_of((dev), struct device_link, link_dev)
|
||||
|
||||
static ssize_t status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *status;
|
||||
|
||||
switch (to_devlink(dev)->status) {
|
||||
case DL_STATE_NONE:
|
||||
status = "not tracked"; break;
|
||||
case DL_STATE_DORMANT:
|
||||
status = "dormant"; break;
|
||||
case DL_STATE_AVAILABLE:
|
||||
status = "available"; break;
|
||||
case DL_STATE_CONSUMER_PROBE:
|
||||
status = "consumer probing"; break;
|
||||
case DL_STATE_ACTIVE:
|
||||
status = "active"; break;
|
||||
case DL_STATE_SUPPLIER_UNBIND:
|
||||
status = "supplier unbinding"; break;
|
||||
default:
|
||||
status = "unknown"; break;
|
||||
}
|
||||
return sprintf(buf, "%s\n", status);
|
||||
}
|
||||
static DEVICE_ATTR_RO(status);
|
||||
|
||||
static ssize_t auto_remove_on_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct device_link *link = to_devlink(dev);
|
||||
char *str;
|
||||
|
||||
if (link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
|
||||
str = "supplier unbind";
|
||||
else if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER)
|
||||
str = "consumer unbind";
|
||||
else
|
||||
str = "never";
|
||||
|
||||
return sprintf(buf, "%s\n", str);
|
||||
}
|
||||
static DEVICE_ATTR_RO(auto_remove_on);
|
||||
|
||||
static ssize_t runtime_pm_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct device_link *link = to_devlink(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(link->flags & DL_FLAG_PM_RUNTIME));
|
||||
}
|
||||
static DEVICE_ATTR_RO(runtime_pm);
|
||||
|
||||
static ssize_t sync_state_only_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct device_link *link = to_devlink(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
|
||||
}
|
||||
static DEVICE_ATTR_RO(sync_state_only);
|
||||
|
||||
static struct attribute *devlink_attrs[] = {
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_auto_remove_on.attr,
|
||||
&dev_attr_runtime_pm.attr,
|
||||
&dev_attr_sync_state_only.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(devlink);
|
||||
|
||||
static void devlink_dev_release(struct device *dev)
|
||||
{
|
||||
kfree(to_devlink(dev));
|
||||
}
|
||||
|
||||
static struct class devlink_class = {
|
||||
.name = "devlink",
|
||||
.owner = THIS_MODULE,
|
||||
.dev_groups = devlink_groups,
|
||||
.dev_release = devlink_dev_release,
|
||||
};
|
||||
|
||||
static int devlink_add_symlinks(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
int ret;
|
||||
size_t len;
|
||||
struct device_link *link = to_devlink(dev);
|
||||
struct device *sup = link->supplier;
|
||||
struct device *con = link->consumer;
|
||||
char *buf;
|
||||
|
||||
len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
|
||||
len += strlen("supplier:") + 1;
|
||||
buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_link(&link->link_dev.kobj, &sup->kobj, "supplier");
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = sysfs_create_link(&link->link_dev.kobj, &con->kobj, "consumer");
|
||||
if (ret)
|
||||
goto err_con;
|
||||
|
||||
snprintf(buf, len, "consumer:%s", dev_name(con));
|
||||
ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf);
|
||||
if (ret)
|
||||
goto err_con_dev;
|
||||
|
||||
snprintf(buf, len, "supplier:%s", dev_name(sup));
|
||||
ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf);
|
||||
if (ret)
|
||||
goto err_sup_dev;
|
||||
|
||||
goto out;
|
||||
|
||||
err_sup_dev:
|
||||
snprintf(buf, len, "consumer:%s", dev_name(con));
|
||||
sysfs_remove_link(&sup->kobj, buf);
|
||||
err_con_dev:
|
||||
sysfs_remove_link(&link->link_dev.kobj, "consumer");
|
||||
err_con:
|
||||
sysfs_remove_link(&link->link_dev.kobj, "supplier");
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void devlink_remove_symlinks(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
struct device_link *link = to_devlink(dev);
|
||||
size_t len;
|
||||
struct device *sup = link->supplier;
|
||||
struct device *con = link->consumer;
|
||||
char *buf;
|
||||
|
||||
sysfs_remove_link(&link->link_dev.kobj, "consumer");
|
||||
sysfs_remove_link(&link->link_dev.kobj, "supplier");
|
||||
|
||||
len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
|
||||
len += strlen("supplier:") + 1;
|
||||
buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
WARN(1, "Unable to properly free device link symlinks!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "supplier:%s", dev_name(sup));
|
||||
sysfs_remove_link(&con->kobj, buf);
|
||||
snprintf(buf, len, "consumer:%s", dev_name(con));
|
||||
sysfs_remove_link(&sup->kobj, buf);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static struct class_interface devlink_class_intf = {
|
||||
.class = &devlink_class,
|
||||
.add_dev = devlink_add_symlinks,
|
||||
.remove_dev = devlink_remove_symlinks,
|
||||
};
|
||||
|
||||
static int __init devlink_class_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = class_register(&devlink_class);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = class_interface_register(&devlink_class_intf);
|
||||
if (ret)
|
||||
class_unregister(&devlink_class);
|
||||
|
||||
return ret;
|
||||
}
|
||||
postcore_initcall(devlink_class_init);
|
||||
|
||||
#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
|
||||
DL_FLAG_AUTOREMOVE_SUPPLIER | \
|
||||
DL_FLAG_AUTOPROBE_CONSUMER | \
|
||||
|
|
@ -407,13 +587,6 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
|
||||
refcount_set(&link->rpm_active, 1);
|
||||
|
||||
if (flags & DL_FLAG_PM_RUNTIME) {
|
||||
if (flags & DL_FLAG_RPM_ACTIVE)
|
||||
refcount_inc(&link->rpm_active);
|
||||
|
||||
pm_runtime_new_link(consumer);
|
||||
}
|
||||
|
||||
get_device(supplier);
|
||||
link->supplier = supplier;
|
||||
INIT_LIST_HEAD(&link->s_node);
|
||||
|
|
@ -423,6 +596,25 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
link->flags = flags;
|
||||
kref_init(&link->kref);
|
||||
|
||||
link->link_dev.class = &devlink_class;
|
||||
device_set_pm_not_required(&link->link_dev);
|
||||
dev_set_name(&link->link_dev, "%s:%s",
|
||||
dev_name(supplier), dev_name(consumer));
|
||||
if (device_register(&link->link_dev)) {
|
||||
put_device(consumer);
|
||||
put_device(supplier);
|
||||
kfree(link);
|
||||
link = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (flags & DL_FLAG_PM_RUNTIME) {
|
||||
if (flags & DL_FLAG_RPM_ACTIVE)
|
||||
refcount_inc(&link->rpm_active);
|
||||
|
||||
pm_runtime_new_link(consumer);
|
||||
}
|
||||
|
||||
/* Determine the initial link state. */
|
||||
if (flags & DL_FLAG_STATELESS)
|
||||
link->status = DL_STATE_NONE;
|
||||
|
|
@ -545,7 +737,7 @@ static void device_link_free(struct device_link *link)
|
|||
|
||||
put_device(link->consumer);
|
||||
put_device(link->supplier);
|
||||
kfree(link);
|
||||
device_unregister(&link->link_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SRCU
|
||||
|
|
@ -1159,6 +1351,9 @@ static void device_links_purge(struct device *dev)
|
|||
{
|
||||
struct device_link *link, *ln;
|
||||
|
||||
if (dev->class == &devlink_class)
|
||||
return;
|
||||
|
||||
mutex_lock(&wfs_lock);
|
||||
list_del(&dev->links.needs_suppliers);
|
||||
mutex_unlock(&wfs_lock);
|
||||
|
|
|
|||
Loading…
Reference in a new issue