【subsys_interface_register】子系统接口注册

转自: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;
}

猜你喜欢

转载自blog.csdn.net/feifei_csdn/article/details/80915742