转自:https://blog.csdn.net/tiantao2012/article/details/52222462
在看cpufreq驱动的时候有遇到
这行code是啥意思呢?
subsys_interface_register(&cpufreq_interface);1576 static struct subsys_interface cpufreq_interface = {
1577 .name = "cpufreq",
1578 .subsys = &cpu_subsys,
1579 .add_dev = cpufreq_add_dev,
1580 .remove_dev = cpufreq_remove_dev,
1581 };
我们看看subsys_interface_register的实现。
1125 int subsys_interface_register(struct subsys_interface *sif)
1126 {
1127 struct bus_type *subsys;
1128 struct subsys_dev_iter iter;
1129 struct device *dev;
1130
1131 if (!sif || !sif->subsys)
1132 return -ENODEV;
1133
1134 subsys = bus_get(sif->subsys);
1135 if (!subsys)
1136 return -EINVAL;
1137
1138 mutex_lock(&subsys->p->mutex);
1139 list_add_tail(&sif->node, &subsys->p->interfaces);
1140 if (sif->add_dev) {
1141 subsys_dev_iter_init(&iter, subsys, NULL, NULL);
1142 while ((dev = subsys_dev_iter_next(&iter)))
1143 sif->add_dev(dev, sif);
1144 subsys_dev_iter_exit(&iter);
1145 }
1146 mutex_unlock(&subsys->p->mutex);
1147
1148 return 0;
1149 }
1134行subsys就等于1578行的cpu_sybsys.
1140行的sif->add_dev 就等1579行的cpufreq_add_dev,所以肯定不为NULL。
1142行会遍历cpu_sybsys,为每一个cpu调用cpufreq_add_dev 方法。也就是有几个cpu,cpufreq_add_dev 方法就没调用几次。
从这里可以看到每个cpu可以有不同的governer.
可以看到对于每个cpu都会调用add_dev函数。
/** * cpufreq_add_dev - the cpufreq interface for a CPU device. * @dev: CPU device. * @sif: Subsystem(子系统) interface structure pointer (not used) */ // 实际上就是每个cpu对应的policy,实际上会遍历每个cpu来调用该函数 static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) { unsigned cpu = dev->id; int ret; dev_dbg(dev, "%s: adding CPU%u\n", __func__, cpu); // 不管cpu_online或者offline,cpufreq_add_dev均会被调用,所以优先判断如果cpu是offline就 // 不用再去创建具体的policy,这个应该是在开机的时候 if (cpu_online(cpu)) { ret = cpufreq_online(cpu); } else { // 针对的是offline的cpu,还是创建policy的连接 /* * A hotplug notifier(通知) will follow and we will handle it as CPU * online then. For now, just create the sysfs link, unless * there is no policy or the link is already present. */ // SMP系统中,一个policy可以管理多个cpu,如果管理cpu创建policy的时候一并创建,后续就只需建立连接 // cpufreq_cpu_data实际上是cpufreq_policy的指针 struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); // 已经有policy并且还没有创建当前cpu的链接 ret = policy && !cpumask_test_and_set_cpu(cpu, policy->real_cpus) ? add_cpu_dev_symlink(policy, cpu) : 0; } return ret; }
对于online的cpu会尝试去创建policy或者policy对应的连接
static int cpufreq_online(unsigned int cpu) { struct cpufreq_policy *policy; bool new_policy; unsigned long flags; unsigned int j; int ret; pr_debug("%s: bringing CPU%u online\n", __func__, cpu); /* Check if this CPU already has a policy to manage it */ //核心层提供cpufreq_cpu_data是个每-cpu变量,用来保存每个cpu使用的cpufreq_policy指针 policy = per_cpu(cpufreq_cpu_data, cpu); if (policy) { WARN_ON(!cpumask_test_cpu(cpu, policy->related_cpus)); // 没有policy需要管理的cpu if (!policy_is_inactive(policy)) return cpufreq_add_policy_cpu(policy, cpu); /* This is the only online CPU for the policy. Start over. */ new_policy = false; down_write(&policy->rwsem); // 感觉不该将cpu赋值给policy->cpu???? // 感觉好像也可以,不一定管理的cpu一定要一直是cpu0 policy->cpu = cpu; policy->governor = NULL; up_write(&policy->rwsem); } else { // 第一次调用的时候,找到第一个cpu一般是cpu0,创建第一个policy new_policy = true; policy = cpufreq_policy_alloc(cpu); if (!policy) return -ENOMEM; } // 将同一个policy管理的cpu掩码放到policy的cpus(online)里面去 cpumask_copy(policy->cpus, cpumask_of(cpu)); /* call driver. From then on the cpufreq must be able * to accept all calls to ->verify and ->setpolicy for this CPU */ // 调用init函数, 与init对应的应该是exit ret = cpufreq_driver->init(policy); if (ret) { pr_debug("initialization failed\n"); goto out_free_policy; } // 信号量是可能引起阻塞的,但是这里多个cpu之间进程上下文进行切换所以使用信号量比较合适 down_write(&policy->rwsem); if (new_policy) { /* related_cpus should at least include policy->cpus. */ cpumask_copy(policy->related_cpus, policy->cpus); //应该查一下cpumask_and函数的function /* Remember CPUs present at the policy creation time. */ cpumask_and(policy->real_cpus, policy->cpus, cpu_present_mask); /* Name and add the kobject */ // 创建了policy0或者policy4 ret = kobject_add(&policy->kobj, cpufreq_global_kobject, "policy%u", cpumask_first(policy->related_cpus)); if (ret) { pr_err("%s: failed to add policy->kobj: %d\n", __func__, ret); goto out_exit_policy; } } /* * affected cpus must always be the one, which are online. We aren't * managing offline cpus here. */ //应该查一下cpumask_and函数的function // 应该是剔除offline的cpu cpumask_and(policy->cpus, policy->cpus, cpu_online_mask); if (new_policy) { policy->user_policy.min = policy->min; policy->user_policy.max = policy->max; write_lock_irqsave(&cpufreq_driver_lock, flags); // 创建new_policy的时候将related的cpu一并创建,只会对online的一起创建 for_each_cpu(j, policy->related_cpus) per_cpu(cpufreq_cpu_data, j) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); } else { policy->min = policy->user_policy.min; policy->max = policy->user_policy.max; } // 并且没有定义setpolicy的函数 if (cpufreq_driver->get && !cpufreq_driver->setpolicy) { // policy->cur的值需要提前被赋值,后续判断是否在table中 policy->cur = cpufreq_driver->get(policy->cpu); if (!policy->cur) { pr_err("%s: ->get() failed\n", __func__); goto out_exit_policy; } } /* * Sometimes boot loaders set CPU frequency to a value outside of * frequency table present with cpufreq core. In such cases CPU might be * unstable if it has to run on that frequency for long duration of time * and so its better to set it to a frequency which is specified in * freq-table. This also makes cpufreq stats inconsistent(不符合) as * cpufreq-stats would fail to register because current frequency of CPU * isn't found in freq-table. * * Because we don't want this change to effect boot process badly, we go * for the next freq which is >= policy->cur ('cur' must be set by now, * otherwise we will end up setting freq to lowest of the table as 'cur' * is initialized to zero). * * We are passing target-freq as "policy->cur - 1" otherwise * __cpufreq_driver_target() would simply fail, as policy->cur will be * equal to target-freq. */ // 定义了target或者是target_index函数 // 在init中会设置policy->cur if ((cpufreq_driver->flags & CPUFREQ_NEED_INITIAL_FREQ_CHECK)&& has_target()) { /* Are we running at unknown frequency ? */ ret = cpufreq_frequency_table_get_index(policy, policy->cur); if (ret == -EINVAL) { /* Warn user and fix it */ pr_warn("%s: CPU%d: Running at unlisted freq: %u KHz\n", __func__, policy->cpu, policy->cur); ret = __cpufreq_driver_target(policy, policy->cur - 1, CPUFREQ_RELATION_L); /* * Reaching here after boot in a few seconds may not * mean that system will remain stable at "unknown" * frequency for longer duration. Hence, a BUG_ON(). */ BUG_ON(ret); pr_warn("%s: CPU%d: Unlisted initial frequency changed to: %u KHz\n", __func__, policy->cpu, policy->cur); } } // 发出CPUFREQ_START通知,通知别的模块已经给cpu准备好了policy blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_START, policy); if (new_policy) { ret = cpufreq_add_dev_interface(policy); if (ret) goto out_exit_policy; blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY, policy); write_lock_irqsave(&cpufreq_driver_lock, flags); list_add(&policy->policy_list, &cpufreq_policy_list); write_unlock_irqrestore(&cpufreq_driver_lock, flags); } // 初始化policy ret = cpufreq_init_policy(policy); if (ret) { pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n", __func__, cpu, ret); /* cpufreq_policy_free() will notify based on this */ new_policy = false; goto out_exit_policy; } up_write(&policy->rwsem); // 向用户空间发送KOBJ_ADD事件 kobject_uevent(&policy->kobj, KOBJ_ADD); /* Callback for handling stuff after policy is ready */ if (cpufreq_driver->ready) cpufreq_driver->ready(policy); pr_debug("initialization complete\n"); return 0; out_exit_policy: up_write(&policy->rwsem); if (cpufreq_driver->exit) cpufreq_driver->exit(policy); out_free_policy: cpufreq_policy_free(policy, !new_policy); return ret; }