最近碰到一个版本在cpu0目录下面没有online节点.check root cause之后,就萌生了将cpu目录下的所有节点全部总结下的想法.
凡是对cpu有点了解的都知道,查看cpu online或者offline状态,cpu的频率信息等等信息都在下面的目录下:
mi9:/sys/devices/system/cpu # ls -l
total 0
drwxr-xr-x 8 root root 0 2019-08-15 22:06 cpu0
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu1
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu2
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu3
drwxr-xr-x 8 root root 0 2019-08-15 22:06 cpu4
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu5
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu6
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu7
drwxr-xr-x 5 root root 0 2019-08-15 22:09 cpufreq
drwxr-xr-x 2 root root 0 2019-08-15 22:24 cpuidle
drwxr-xr-x 2 root root 0 2019-08-15 22:24 hotplug
-r--r--r-- 1 root root 4096 2019-08-15 22:24 isolated
-r--r--r-- 1 root root 4096 2019-08-15 22:24 kernel_max
-r--r--r-- 1 root root 4096 2019-08-15 22:24 modalias
-r--r--r-- 1 root root 4096 2019-08-15 22:24 offline
-r--r--r-- 1 root root 4096 2019-08-15 22:24 online
-r--r--r-- 1 root root 4096 2019-08-15 22:24 possible
drwxr-xr-x 2 root root 0 2019-08-15 22:24 power
-r--r--r-- 1 root root 4096 2019-08-15 22:24 present
-rw-r--r-- 1 root root 4096 2019-08-15 22:24 uevent
对于各个cpuX下的有如下节点:
mi9:/sys/devices/system/cpu # ls cpu0/ -l
total 0
-rw-r--r-- 1 root root 4096 2019-08-15 22:26 cpu_capacity
lrwxrwxrwx 1 root root 0 2019-08-15 22:09 cpufreq -> ../cpufreq/policy0
drwxr-xr-x 5 root root 0 2019-08-15 22:26 cpuidle
drwxr-xr-x 2 root root 0 2019-08-15 22:26 hotplug
lrwxrwxrwx 1 root root 0 2019-08-15 22:26 of_node -> ../../../../firmware/devicetree/base/cpus/cpu@0
-rw-r--r-- 1 root root 4096 2019-08-15 22:26 online
drwxr-xr-x 2 root root 0 2019-08-15 22:26 power
drwxr-xr-x 3 root root 0 2019-08-15 22:26 regs
lrwxrwxrwx 1 root root 0 2019-08-15 22:26 subsystem -> ../../../../bus/cpu
drwxr-xr-x 2 root root 0 2019-08-15 22:26 topology
-rw-r--r-- 1 root root 4096 2019-08-15 22:26 uevent
那么上面的所有的这些节点信息怎么来的呢?
1 cpu目录的节点信息
我们想知道下面的节点信息如何得来的:
mi9:/sys/devices/system/cpu # ls -l
total 0
drwxr-xr-x 8 root root 0 2019-08-15 22:06 cpu0
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu1
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu2
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu3
drwxr-xr-x 8 root root 0 2019-08-15 22:06 cpu4
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu5
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu6
drwxr-xr-x 7 root root 0 2019-08-15 22:06 cpu7
drwxr-xr-x 5 root root 0 2019-08-15 22:09 cpufreq
drwxr-xr-x 2 root root 0 2019-08-15 22:24 cpuidle
drwxr-xr-x 2 root root 0 2019-08-15 22:24 hotplug
-r--r--r-- 1 root root 4096 2019-08-15 22:24 isolated
-r--r--r-- 1 root root 4096 2019-08-15 22:24 kernel_max
-r--r--r-- 1 root root 4096 2019-08-15 22:24 modalias
-r--r--r-- 1 root root 4096 2019-08-15 22:24 offline
-r--r--r-- 1 root root 4096 2019-08-15 22:24 online
-r--r--r-- 1 root root 4096 2019-08-15 22:24 possible
drwxr-xr-x 2 root root 0 2019-08-15 22:24 power
-r--r--r-- 1 root root 4096 2019-08-15 22:24 present
-rw-r--r-- 1 root root 4096 2019-08-15 22:24 uevent
分析如下:
1.1 cpuX 目录的创建
代码流程如下:cpu_dev_init—> cpu_dev_register_generic—>register_cpu
void __init cpu_dev_init(void)
{
if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
panic("Failed to register CPU subsystem");
cpu_dev_register_generic();
cpu_register_vulnerabilities();
}
static void __init cpu_dev_register_generic(void)
{
#ifdef CONFIG_GENERIC_CPU_DEVICES
int i;
for_each_possible_cpu(i) {
/*给每个cpu注册一个sys节点*/
if (register_cpu(&per_cpu(cpu_devices, i), i))
panic("Failed to register CPU device");
}
#endif
}
/*
* register_cpu - Setup a sysfs device for a CPU.
* @cpu - cpu->hotpluggable field set to 1 will generate a control file in
* sysfs for this CPU.
* @num - CPU number to use when creating the device.
*
* Initialize and register the CPU device.
*/
int register_cpu(struct cpu *cpu, int num)
{
int error;
cpu->node_id = cpu_to_node(num);
memset(&cpu->dev, 0x00, sizeof(struct device));
cpu->dev.id = num;
cpu->dev.bus = &cpu_subsys;
cpu->dev.release = cpu_device_release;
cpu->dev.offline_disabled = !cpu->hotpluggable;
cpu->dev.offline = !cpu_online(num);
cpu->dev.of_node = of_get_cpu_node(num, NULL);
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
cpu->dev.bus->uevent = cpu_uevent;
#endif
cpu->dev.groups = common_cpu_attr_groups;
if (cpu->hotpluggable)
cpu->dev.groups = hotplugable_cpu_attr_groups;
/*这个是核心,注册每个cpuX,并且会创建cpuX目录下的相关节点信息*/
error = device_register(&cpu->dev);
if (error)
return error;
per_cpu(cpu_sys_devices, num) = &cpu->dev;
register_cpu_under_node(num, cpu_to_node(num));
dev_pm_qos_expose_latency_limit(&cpu->dev, 0);
return 0;
}
/*这是一个全局性bus_type,后面在cpu目录下创建的除了cpuX之外的所有目录都会使用这个bus type*/
struct bus_type cpu_subsys = {
.name = "cpu",
.dev_name = "cpu",
.match = cpu_subsys_match,
#ifdef CONFIG_HOTPLUG_CPU
.online = cpu_subsys_online,
.offline = cpu_subsys_offline,
#endif
};
EXPORT_SYMBOL_GPL(cpu_subsys);
我们来看看device_register的实现,实现在driver/base/core.c文件里面:
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);
int device_add(struct device *dev)
{
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
/*这里设置了cpuX的文件名字,在register_cpu里面,dev_name,dev->id都被赋值了*/
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
.....................
}
所以到这里cpuX创建完毕
1.2 cpufreq/cpuidle/hotplug/power目录的创建
1.2.1 cpufreq的创建
mi9:/sys/devices/system/cpu # ls cpufreq/ -l
total 0
-rw-r--r-- 1 root root 4096 2019-08-15 23:14 boost
drwxr-xr-x 4 root root 0 2019-08-15 22:09 policy0
drwxr-xr-x 4 root root 0 2019-08-15 22:09 policy4
代码实现:
struct kobject *cpufreq_global_kobject;
EXPORT_SYMBOL(cpufreq_global_kobject);
static int __init cpufreq_core_init(void)
{
if (cpufreq_disabled())
return -ENODEV;
/*这里创建cpufreq目录,已经全局性的kobject对象,这样对于其他模块也是可以在此目录下创建响应的文件的*/
cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);
BUG_ON(!cpufreq_global_kobject);
register_syscore_ops(&cpufreq_syscore_ops);
return 0;
}
/*创建boost节点*/
static int create_boost_sysfs_file(void)
{
int ret;
ret = sysfs_create_file(cpufreq_global_kobject, &boost.attr);
if (ret)
pr_err("%s: cannot register global BOOST sysfs file\n",
┊ ┊ __func__);
return ret;
}
static void remove_boost_sysfs_file(void)
{
if (cpufreq_boost_supported())
sysfs_remove_file(cpufreq_global_kobject, &boost.attr);
}
static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
{
struct cpufreq_policy *policy;
int ret;
policy = kzalloc(sizeof(*policy), GFP_KERNEL);
if (!policy)
return NULL;
if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
goto err_free_policy;
if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
goto err_free_cpumask;
if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL))
goto err_free_rcpumask;
/*创建policyX节点信息,根据cluster来创建*/
ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
┊ cpufreq_global_kobject, "policy%u", cpu);
if (ret) {
pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret);
goto err_free_real_cpus;
}
................
}
至于policyX的目录下的信息,在driver/cpufreq/cpufreq.c文件中.包含频率信息,governor信息等等.
1.2.2 cpuidle的创建
mi9:/sys/devices/system/cpu # ls cpuidle/ -l
total 0
-r--r--r-- 1 root root 4096 2019-08-15 23:00 current_driver
-r--r--r-- 1 root root 4096 2019-08-15 23:00 current_governor_ro
可以看到下面的代码创建上面的目录以及节点信息:
static int __init cpuidle_init(void)
{
int ret;
if (cpuidle_disabled())
return -ENODEV;
/*关键的cpu_subsys的使用,可以在cpu目录下创建文件*/
ret = cpuidle_add_interface(cpu_subsys.dev_root);
if (ret)
return ret;
latency_notifier_init(&cpuidle_latency_notifier);
return 0;
}
int cpuidle_add_interface(struct device *dev)
{
if (sysfs_switch)
cpuidle_attr_group.attrs = cpuidle_switch_attrs;
return sysfs_create_group(&dev->kobj, &cpuidle_attr_group);
}
static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);
static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL);
static struct attribute *cpuidle_default_attrs[] = {
&dev_attr_current_driver.attr,
&dev_attr_current_governor_ro.attr,
NULL
};
static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL);
static DEVICE_ATTR(current_governor, 0644, show_current_governor,
store_current_governor);
static struct attribute *cpuidle_switch_attrs[] = {
&dev_attr_available_governors.attr,
&dev_attr_current_driver.attr,
&dev_attr_current_governor.attr,
NULL
};
static struct attribute_group cpuidle_attr_group = {
.attrs = cpuidle_default_attrs,
.name = "cpuidle",
};
上面写的很清楚,不赘述.
1.2.3 hotplug的创建
节点信息如下:
mi9:/sys/devices/system/cpu # ls hotplug/ -l
total 0
-r--r--r-- 1 root root 4096 2019-08-15 22:56 states
代码实现在driver/base/cpu.c文件里面:
static int __init cpuhp_sysfs_init(void)
{
int cpu, ret;
ret = cpu_smt_state_init();
if (ret)
return ret;
/*这个是创建cpu目录下的节点信息*/
ret = sysfs_create_group(&cpu_subsys.dev_root->kobj,
&cpuhp_cpu_root_attr_group);
if (ret)
return ret;
for_each_possible_cpu(cpu) {
struct device *dev = get_cpu_device(cpu);
if (!dev)
continue;
/*这个是创建cpuX/hotplug目录及其此目录的节点信息,后面讲解*/
ret = sysfs_create_group(&dev->kobj, &cpuhp_cpu_attr_group);
if (ret)
return ret;
}
return 0;
}
device_initcall(cpuhp_sysfs_init);
static const struct attribute_group cpuhp_cpu_root_attr_group = {
.attrs = cpuhp_cpu_root_attrs,
.name = "hotplug",
NULL
};
static ssize_t show_cpuhp_states(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t cur, res = 0;
int i;
mutex_lock(&cpuhp_state_mutex);
for (i = CPUHP_OFFLINE; i <= CPUHP_ONLINE; i++) {
struct cpuhp_step *sp = cpuhp_get_step(i);
if (sp->name) {
cur = sprintf(buf, "%3d: %s\n", i, sp->name);
buf += cur;
res += cur;
}
}
mutex_unlock(&cpuhp_state_mutex);
return res;
}
static DEVICE_ATTR(states, 0444, show_cpuhp_states, NULL);
static struct attribute *cpuhp_cpu_root_attrs[] = {
&dev_attr_states.attr,
NULL
};
static const struct attribute_group cpuhp_cpu_root_attr_group = {
.attrs = cpuhp_cpu_root_attrs,
.name = "hotplug",
NULL
};
比较简单,一目了然.
1.2.4 power的创建
power 的节点信息如下:
mi11:/sys/devices/system/cpu # ls power/ -l
total 0
-rw-r--r-- 1 root root 4096 2019-08-15 23:30 autosuspend_delay_ms
-rw-r--r-- 1 root root 4096 2019-08-15 23:30 control
-r--r--r-- 1 root root 4096 2019-08-15 23:30 runtime_active_time
-r--r--r-- 1 root root 4096 2019-08-15 23:30 runtime_status
-r--r--r-- 1 root root 4096 2019-08-15 23:30 runtime_suspended_time
代码如下:
static struct attribute *runtime_attrs[] = {
#ifndef CONFIG_PM_ADVANCED_DEBUG
&dev_attr_runtime_status.attr,
#endif
&dev_attr_control.attr,
&dev_attr_runtime_suspended_time.attr,
&dev_attr_runtime_active_time.attr,
&dev_attr_autosuspend_delay_ms.attr,
NULL,
};
static const struct attribute_group pm_runtime_attr_group = {
.name = power_group_name,
.attrs = runtime_attrs,
};
int dpm_sysfs_add(struct device *dev)
{
int rc;
rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
if (rc)
return rc;
if (pm_runtime_callbacks_present(dev)) {
rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
if (rc)
goto err_out;
....................
}
int device_add(struct device *dev)
{
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
...............
error = dpm_sysfs_add(dev);
...............
}
其实上面的过程也是cpuX/power类似的创建过程,不在赘述.
1.3 其他节点的创建
这些节点信息比较简单,直接在driver/base/cpu.c文件里面创建,具体如下:
void __init cpu_dev_init(void)
{
if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
panic("Failed to register CPU subsystem");
cpu_dev_register_generic();
cpu_register_vulnerabilities();
}
/* Keep in sync with cpu_subsys_attrs */
static struct cpu_attr cpu_attrs[] = {
_CPU_ATTR(online, &__cpu_online_mask),
_CPU_ATTR(possible, &__cpu_possible_mask),
_CPU_ATTR(present, &__cpu_present_mask),
};
static struct attribute *cpu_root_attrs[] = {
#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
&dev_attr_probe.attr,
&dev_attr_release.attr,
#endif
&cpu_attrs[0].attr.attr,
&cpu_attrs[1].attr.attr,
&cpu_attrs[2].attr.attr,
&dev_attr_kernel_max.attr,
&dev_attr_offline.attr,
&dev_attr_isolated.attr,
#ifdef CONFIG_NO_HZ_FULL
&dev_attr_nohz_full.attr,
#endif
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
&dev_attr_modalias.attr,
#endif
NULL
};
static struct attribute_group cpu_root_attr_group = {
.attrs = cpu_root_attrs,
};
static const struct attribute_group *cpu_root_attr_groups[] = {
&cpu_root_attr_group,
NULL,
};
至此,cpu目录下全部讲解完毕.
2 cpuX目录下的节点信息
cpuX目录下的节点信息如下:
miX:/sys/devices/system/cpu/cpu0 # ls -l
total 0
-rw-r--r-- 1 root root 4096 2019-08-15 22:26 cpu_capacity
lrwxrwxrwx 1 root root 0 2019-08-15 22:09 cpufreq -> ../cpufreq/policy0
drwxr-xr-x 5 root root 0 2019-08-15 22:26 cpuidle
drwxr-xr-x 2 root root 0 2019-08-15 22:26 hotplug
-r--r--r-- 1 root root 4096 2019-08-15 22:26 isolate
lrwxrwxrwx 1 root root 0 2019-08-15 22:26 of_node -> ../../../../firmware/devicetree/base/cpus/cpu@0
-rw-r--r-- 1 root root 4096 2019-08-15 22:26 online
drwxr-xr-x 2 root root 0 2019-08-15 22:26 power
drwxr-xr-x 3 root root 0 2019-08-15 22:26 regs
lrwxrwxrwx 1 root root 0 2019-08-15 22:26 subsystem -> ../../../../bus/cpu
drwxr-xr-x 2 root root 0 2019-08-15 22:26 topology
-rw-r--r-- 1 root root 4096 2019-08-15 22:26 uevent
2.1 cpu_capacity
具体实现在driver/base/arch_topology.c文件中,这个数值是每个cpu的工作能力,数值越大,性能越好.
static int register_cpu_capacity_sysctl(void)
{
int i;
struct device *cpu;
for_each_possible_cpu(i) {
cpu = get_cpu_device(i);
if (!cpu) {
pr_err("%s: too early to get CPU%d device!\n",
┊ ┊ __func__, i);
continue;
}
device_create_file(cpu, &dev_attr_cpu_capacity);
}
return 0;
}
subsys_initcall(register_cpu_capacity_sysctl);
2.2 cpufreq目录
我们能够看到此目录是link到/sys/devices/system/cpu/cpufreq/policy0/上,上面已经知道此目录怎么来的,只需要知道怎么link的就OK.
int cpufreq_register_driver(struct cpufreq_driver *driver_data)
{
unsigned long flags;
int ret;
if (cpufreq_disabled())
return -ENODEV;
........................
ret = subsys_interface_register(&cpufreq_interface);
........................
}
static struct subsys_interface cpufreq_interface = {
.name = "cpufreq",
/*在每个cpuX目录下创建cpufreq*/
.subsys = &cpu_subsys,
.add_dev = cpufreq_add_dev,
.remove_dev = cpufreq_remove_dev,
};
static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
struct cpufreq_policy *policy;
unsigned cpu = dev->id;
int ret;
dev_dbg(dev, "%s: adding CPU%u\n", __func__, cpu);
if (cpu_online(cpu)) {
ret = cpufreq_online(cpu);
if (ret)
return ret;
}
/* Create sysfs link on CPU registration */
policy = per_cpu(cpufreq_cpu_data, cpu);
if (policy)
add_cpu_dev_symlink(policy, cpu);
return 0;
}
static void add_cpu_dev_symlink(struct cpufreq_policy *policy, unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
if (!dev)
return;
if (cpumask_test_and_set_cpu(cpu, policy->real_cpus))
return;
dev_dbg(dev, "%s: Adding symlink\n", __func__);
/*此cpufreq目录link到policy->kobj目录*/
if (sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq"))
dev_err(dev, "cpufreq symlink creation failed\n");
}
2.3 cpuidle目录
节点信息如下:
miX:/sys/devices/system/cpu/cpu0 # ls cpuidle/ -l
total 0
drwxr-xr-x 2 root root 0 2019-08-15 23:01 driver
drwxr-xr-x 2 root root 0 2019-08-15 23:01 state0
drwxr-xr-x 2 root root 0 2019-08-15 23:01 state1
代码实现如下:
static int __init arm_idle_init(void)
{
int cpu, ret;
struct cpuidle_driver *drv;
struct cpuidle_device *dev;
for_each_possible_cpu(cpu) {
drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL);
.................
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
ret = -ENOMEM;
goto out_unregister_drv;
}
dev->cpu = cpu;
ret = cpuidle_register_device(dev);
if (ret) {
pr_err("Failed to register cpuidle device for CPU %d\n",
┊ ┊ cpu);
goto out_kfree_dev;
}
..............
}
int cpuidle_register_device(struct cpuidle_device *dev)
{
int ret = -EBUSY;
.................
ret = cpuidle_enable_device(dev);
if (ret)
goto out_sysfs;
.................
}
int cpuidle_enable_device(struct cpuidle_device *dev)
{
...............
ret = cpuidle_add_device_sysfs(dev);
if (ret)
return ret;
..............
}
int cpuidle_add_device_sysfs(struct cpuidle_device *device)
{
int ret;
ret = cpuidle_add_state_sysfs(device);
if (ret)
return ret;
ret = cpuidle_add_driver_sysfs(device);
if (ret)
cpuidle_remove_state_sysfs(device);
return ret;
}
static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
{
int i, ret = -ENOMEM;
struct cpuidle_state_kobj *kobj;
struct cpuidle_device_kobj *kdev = device->kobj_dev;
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device);
/* state statistics */
for (i = 0; i < drv->state_count; i++) {
kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
if (!kobj) {
ret = -ENOMEM;
goto error_state;
}
kobj->state = &drv->states[i];
kobj->state_usage = &device->states_usage[i];
init_completion(&kobj->kobj_unregister);
ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle,
┊ &kdev->kobj, "state%d", i);
if (ret) {
kfree(kobj);
goto error_state;
}
kobject_uevent(&kobj->kobj, KOBJ_ADD);
device->kobjs[i] = kobj;
}
return 0;
error_state:
for (i = i - 1; i >= 0; i--)
cpuidle_free_state_kobj(device, i);
return ret;
}
static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)
{
struct cpuidle_driver_kobj *kdrv;
struct cpuidle_device_kobj *kdev = dev->kobj_dev;
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
int ret;
kdrv = kzalloc(sizeof(*kdrv), GFP_KERNEL);
if (!kdrv)
return -ENOMEM;
kdrv->drv = drv;
init_completion(&kdrv->kobj_unregister);
ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle,
┊ &kdev->kobj, "driver");
if (ret) {
kfree(kdrv);
return ret;
}
kobject_uevent(&kdrv->kobj, KOBJ_ADD);
dev->kobj_driver = kdrv;
return ret;
}
2.4 power目录
2.5 hotplug目录
2.4和2.5与第一章类似
2.6 online节点
这个节点重点讲解,很有意思,如下:
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);
int device_add(struct device *dev)
{
.........
error = device_add_attrs(dev);
.........
}
int register_cpu(struct cpu *cpu, int num)
{
............
error = device_register(&cpu->dev);
............
}
static int device_add_attrs(struct device *dev)
{
struct class *class = dev->class;
const struct device_type *type = dev->type;
int error;
....................
/*这里只要符合调节就可以创建online调节*/
if (device_supports_offline(dev) && !dev->offline_disabled) {
error = device_create_file(dev, &dev_attr_online);
if (error)
goto err_remove_dev_groups;
}
return 0;
..............
}
static ssize_t online_show(struct device *dev, struct device_attribute *attr,
┊ char *buf)
{
bool val;
device_lock(dev);
val = !dev->offline;
device_unlock(dev);
return sprintf(buf, "%u\n", val);
}
static ssize_t online_store(struct device *dev, struct device_attribute *attr,
┊ const char *buf, size_t count)
{
bool val;
int ret;
ret = strtobool(buf, &val);
if (ret < 0)
return ret;
ret = lock_device_hotplug_sysfs();
if (ret)
return ret;
ret = val ? device_online(dev) : device_offline(dev);
unlock_device_hotplug();
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(online);
2.7 of_node和subsystem节点
过程与online节点创建走的是同一个路径
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);
int device_add(struct device *dev)
{
.........
error = device_add_class_symlinks(dev);
.........
}
int register_cpu(struct cpu *cpu, int num)
{
............
error = device_register(&cpu->dev);
...........
}
static int device_add_class_symlinks(struct device *dev)
{
struct device_node *of_node = dev_of_node(dev);
int error;
if (of_node) {
error = sysfs_create_link(&dev->kobj, &of_node->kobj,"of_node");
if (error)
dev_warn(dev, "Error %d creating of_node link\n",error);
/* An error here doesn't warrant bringing down the device */
}
if (!dev->class)
return 0;
error = sysfs_create_link(&dev->kobj,
┊ &dev->class->p->subsys.kobj,
┊ "subsystem");
............
}
2.8 topology目录
这个一个非常重要的目录
具体信息:
MIX5:/sys/devices/system/cpu/cpu0 # ls topology/ -l
total 0
-r--r--r-- 1 root root 4096 2019-08-15 22:57 core_id
-r--r--r-- 1 root root 4096 2019-08-15 22:57 core_siblings
-r--r--r-- 1 root root 4096 2019-08-15 22:57 core_siblings_list
-r--r--r-- 1 root root 4096 2019-08-15 22:57 physical_package_id
-r--r--r-- 1 root root 4096 2019-08-15 22:57 thread_siblings
-r--r--r-- 1 root root 4096 2019-08-15 22:57 thread_siblings_list
具体含义如下:
- core_id:某个cluster内的cpu id(从0开始)
- core_siblings:处于同一个cluster内的所有cpu的cpumask bit位
- core_siblings_list:bit位转化为list id显示
- physical_package_id: cluster id
- thread_siblings:cpu具体处在bit位的哪一个位置
- thread_siblings_list:系统cpu id号
接口代码如下:
static struct attribute *default_attrs[] = {
&dev_attr_physical_package_id.attr,
&dev_attr_core_id.attr,
&dev_attr_thread_siblings.attr,
&dev_attr_thread_siblings_list.attr,
&dev_attr_core_siblings.attr,
&dev_attr_core_siblings_list.attr,
#ifdef CONFIG_SCHED_BOOK
&dev_attr_book_id.attr,
&dev_attr_book_siblings.attr,
&dev_attr_book_siblings_list.attr,
#endif
#ifdef CONFIG_SCHED_DRAWER
&dev_attr_drawer_id.attr,
&dev_attr_drawer_siblings.attr,
&dev_attr_drawer_siblings_list.attr,
#endif
NULL
};
static const struct attribute_group topology_attr_group = {
.attrs = default_attrs,
.name = "topology"
};
/* Add/Remove cpu_topology interface for CPU device */
static int topology_add_dev(unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
return sysfs_create_group(&dev->kobj, &topology_attr_group);
}
static int topology_remove_dev(unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
sysfs_remove_group(&dev->kobj, &topology_attr_group);
return 0;
}
static int topology_sysfs_init(void)
{
return cpuhp_setup_state(CPUHP_TOPOLOGY_PREPARE,
"base/topology:prepare", topology_add_dev,
topology_remove_dev);
}
device_initcall(topology_sysfs_init);
上面仅仅是实现接口 的代码,具体的topology下面各个节点的数值是在arch/arm64/kernel/topology.c文件中实现的, 当然是通过读取dts里面的配置信息得来的.
over!!!