欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

剖析与实现Linux热管理框架深度解析

最编程 2024-07-24 12:40:15
...

关键词:Zone、Cooling、Governor、Step Wise、Fair Share、trip等等。

Linux Thermal的目的是控制系统运行过程中采样点温度,避免温度过高造成器件损坏,确保芯片长期稳定工作。

整个Thermal框架可以分为四部分:

  • Thermal Driver负责将获取温度设备,注册成struct thermal_zone_device,比如Temp Sensor、NTC等。
  • Thermal Governor则负责如何控制温度,注册成struct thermal_governor,比如Step Wise、Bang Bang等等。
  • Thermal Cooling负责将控制温度设备,注册成struct thermal_cooling_device,比如风扇、CPU、DDR、GPU等。
  • Thermal Core则是Thermal Driver、Thermal Governor、Thermal Governor的粘合剂,同时提供了用户空间sysfs节点等通用功能。

所以Thermal的工作流程是通过Thermal Driver获取温度,然后经过Thermal Governor决策,最后通过Thermal Cooling执行温度控制。

下面首先从总体详细分析Thermal框架以及数据结构,然后分别分析Thermal Driver实例、Thermal Governor(Step Wise)、以及Thermal Cooling实例。

最后将这些内容串起来,分析Thermal是如何控制温度的。

1. Thermal框架分析

1.1 Thermal数据结构

struct thermal_zone_device是对获取温度设备的抽象,成员ops是对该Thermal Zone操作的抽象;governor是该Thermal Zone所使用的调温策略;thermal_instances是该Thermal Zone下的Cooling Device列表。

struct thermal_zone_device {
    int id;
    char type[THERMAL_NAME_LENGTH];
    struct device device;
    struct thermal_attr *trip_temp_attrs;
    struct thermal_attr *trip_type_attrs;
    struct thermal_attr *trip_hyst_attrs;
    void *devdata;
    int trips;---------------------------------------------------------thermal zone支持的trip数目。
    unsigned long trips_disabled;    /* bitmap for disabled trips */
    int passive_delay;
    int polling_delay;-------------------------------------------------轮询读取温度的建个,0表示采用中断形式。
    int temperature;---------------------------------------------------当前温度。
    int last_temperature;----------------------------------------------最近一次温度。
    int emul_temperature;
    int passive;
    int prev_low_trip;
    int prev_high_trip;
    unsigned int forced_passive;
    atomic_t need_update;
    struct thermal_zone_device_ops *ops;------------------------------当前thermal zone操作函数集。
    struct thermal_zone_params *tzp;----------------------------------当前thermal zone参数。
    struct thermal_governor *governor;
    void *governor_data;
    struct list_head thermal_instances;-------------------------------当前thermal zone上thermal_instances列表。
    struct idr idr;
    struct mutex lock;
    struct list_head node;
    struct delayed_work poll_queue;
    enum thermal_notify_event notify_event;
};

struct thermal_zone_device_ops {
    int (*bind) (struct thermal_zone_device *,
             struct thermal_cooling_device *);------------------------将cooling device绑定到thermal zone中,两者通过struct thermal_instances在thermal_zone_bind_cooling_device()中绑定。
    int (*unbind) (struct thermal_zone_device *,
               struct thermal_cooling_device *);
    int (*get_temp) (struct thermal_zone_device *, int *);
    int (*set_trips) (struct thermal_zone_device *, int, int);
    int (*get_mode) (struct thermal_zone_device *,
             enum thermal_device_mode *);
    int (*set_mode) (struct thermal_zone_device *,
        enum thermal_device_mode);
    int (*get_trip_type) (struct thermal_zone_device *, int,
        enum thermal_trip_type *);
    int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
    int (*set_trip_temp) (struct thermal_zone_device *, int, int);
    int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
    int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
    int (*get_crit_temp) (struct thermal_zone_device *, int *);
    int (*set_emul_temp) (struct thermal_zone_device *, int);
    int (*get_trend) (struct thermal_zone_device *, int,
              enum thermal_trend *);
    int (*notify) (struct thermal_zone_device *, int,
               enum thermal_trip_type);
};

struct thermal_bind_params {
    struct thermal_cooling_device *cdev;
    int weight;
    int trip_mask;
    unsigned long *binding_limits;
    int (*match) (struct thermal_zone_device *tz,
            struct thermal_cooling_device *cdev);
};

struct thermal_zone_params {
    char governor_name[THERMAL_NAME_LENGTH];
    bool no_hwmon;
    int num_tbps;    /* Number of tbp entries */
    struct thermal_bind_params *tbp;
...
    int slope;
    int offset;
};

struct thermal_zone_of_device_ops {
    int (*get_temp)(void *, int *);
    int (*get_trend)(void *, int, enum thermal_trend *);
    int (*set_trips)(void *, int, int);
    int (*set_emul_temp)(void *, int);
    int (*set_trip_temp)(void *, int, int);
};

struct thermal_cooling_device是对降温设备的抽象,对风扇设备就是不同的转速,对CPU、DDR、GPU就是不同的电压或者频率。

struct thermal_cooling_device_ops是Cooling Device操作函数集,其中set_cur_state()是对设备进行温度控制。

struct thermal_cooling_device {
    int id;
    char type[THERMAL_NAME_LENGTH];
    struct device device;
    struct device_node *np;
    void *devdata;
    const struct thermal_cooling_device_ops *ops;
    bool updated; /* true if the cooling device does not need update */
    struct mutex lock; /* protect thermal_instances list */
    struct list_head thermal_instances;
    struct list_head node;
};

struct thermal_cooling_device_ops {
    int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
    int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
    int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
...
};

strcut thermal_governor是对温控策略的抽象,也就是根据Thermal Zone的trip来选择Thermal Cooling设备的行为。比如,温度越高风扇转速越快;温度越高CPU运行在更低电压和频率上。

struct thermal_governor {
    char name[THERMAL_NAME_LENGTH];
    int (*bind_to_tz)(struct thermal_zone_device *tz);---------------------将一个governor绑定到thermal zone得一个trip上。
    void (*unbind_from_tz)(struct thermal_zone_device *tz);----------------将一个governor从thermal zone解绑。
    int (*throttle)(struct thermal_zone_device *tz, int trip);-------------根据trip遍历当前thermal zone下所有的cooling device执行温控策略。
    struct list_head    governor_list;-------------------------------------thermal_governor_list上的一个列表元素。
};

所有的策略选择都是通过throttle()函数进行的,不同的Governor的区别也主要在这里。内核已经实现了Step Wise、User等等,并且还在演进中。

通过struct thermal_instances可以将thermal zone和thermal cooling设备绑定起来。

struct thermal_instance {
    int id;
    char name[THERMAL_NAME_LENGTH];
    struct thermal_zone_device *tz;-------------------------------------------绑定的thermal zone。
    struct thermal_cooling_device *cdev;--------------------------------------绑定的thermal cooling设备。
    int trip;-----------------------------------------------------------------对应的thermal zone的trip。
    bool initialized;
    unsigned long upper;    /* Highest cooling state for this trip point */---cooling设备的最高降温状态。
    unsigned long lower;    /* Lowest cooling state for this trip point */----cooling设备最低降温状态。
    unsigned long target;    /* expected cooling state */---------------------cooling设备的当前状态,也是thermal_cooling_device_ops->set_cur_state()设置后的值。
    char attr_name[THERMAL_NAME_LENGTH];
    struct device_attribute attr;
    char weight_attr_name[THERMAL_NAME_LENGTH];
    struct device_attribute weight_attr;
    struct list_head tz_node; /* node in tz->thermal_instances */-------------thermal_zone_device->thermal_instances上的节点。
    struct list_head cdev_node; /* node in cdev->thermal_instances */---------thermal_cooling_device->thermal_instances上的节点。
    unsigned int weight; /* The weight of the cooling device */
};

thermal_device_mode表示当前的thermal zone是否使能。

thermal_trip_type表示thermal zone的当前trip类型,其中ACTIVE和PASSIVE属于non-critical类型,交由Governor进行处理;HOT和CRITICAL属于critical类型,其中CRITICAL会执行orderly_poweroff()。

thermal_trend表示thermal zone的温度趋势,是平缓、上升、下降还是跳跃式的,这就给Governor选择trip提供依据。

enum thermal_device_mode {
    THERMAL_DEVICE_DISABLED = 0,
    THERMAL_DEVICE_ENABLED,
};

enum thermal_trip_type {
    THERMAL_TRIP_ACTIVE = 0,
    THERMAL_TRIP_PASSIVE,
    THERMAL_TRIP_HOT,
    THERMAL_TRIP_CRITICAL,
};

enum thermal_trend {
    THERMAL_TREND_STABLE, /* temperature is stable */-----------------------表示温度平稳。
    THERMAL_TREND_RAISING, /* temperature is raising */---------------------表示当前温度趋势是升高的。
    THERMAL_TREND_DROPPING, /* temperature is dropping */-------------------表示当前温度趋势是降低的。
    THERMAL_TREND_RAISE_FULL, /* apply highest cooling action */------------直接应用upper对应的trip。
    THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */--------------直接应用lower对应的trip。
};

/* Thermal notification reason */
enum thermal_notify_event {
    THERMAL_EVENT_UNSPECIFIED, /* Unspecified event */
    THERMAL_EVENT_TEMP_SAMPLE, /* New Temperature sample */
    THERMAL_TRIP_VIOLATED, /* TRIP Point violation */
    THERMAL_TRIP_CHANGED, /* TRIP Point temperature changed */
    THERMAL_DEVICE_DOWN, /* Thermal device is down */
    THERMAL_DEVICE_UP, /* Thermal device is up after a down event */
    THERMAL_DEVICE_POWER_CAPABILITY_CHANGED, /* power capability changed */
};

1.2 Thermal Core APIs

Thermal core是Thermal Zone、Thermal Cooling、ThermalGovernor的粘合剂。

通过Thermal core提供的API,将这三者相互关联起来;从Thermal Zone设备获取温度,选择对应的Thermal Governor,Thermal Governor设置Thermal Cooling的状态,进而达到控制温度的目的。

通过thermal_zone_device_register()注册thermal zone设备,创建一系列sysfs节点,并且和governor、cooling进行绑定。

struct thermal_zone_device *thermal_zone_device_register(const char *type,
    int trips, int mask, void *devdata,
    struct thermal_zone_device_ops *ops,
    struct thermal_zone_params *tzp,
    int passive_delay, int polling_delay)
{
    struct thermal_zone_device *tz;
    enum thermal_trip_type trip_type;
    int trip_temp;
    int result;
    int count;
    int passive = 0;
    struct thermal_governor *governor;

    if (type && strlen(type) >= THERMAL_NAME_LENGTH)
        return ERR_PTR(-EINVAL);

    if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips)
        return ERR_PTR(-EINVAL);

    if (!ops)
        return ERR_PTR(-EINVAL);

    if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
        return ERR_PTR(-EINVAL);

    tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
    if (!tz)
        return ERR_PTR(-ENOMEM);

    INIT_LIST_HEAD(&tz->thermal_instances);------------------------------初始化thermal_instances链表,放置struct thermal_instances实例。通过thermal_instances可以关联thermal zone和thermal cooling。
    idr_init(&tz->idr);
    mutex_init(&tz->lock);
    result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
    if (result) {
        kfree(tz);
        return ERR_PTR(result);
    }

    strlcpy(tz->type, type ? : "", sizeof(tz->type));
    tz->ops = ops;
    tz->tzp = tzp;
    tz->device.class = &thermal_class;------------------------------------创建的设备会在/sys/class/thermal下面有个链接。
    tz->devdata = devdata;
    tz->trips = trips;
    tz->passive_delay = passive_delay;
    tz->polling_delay = polling_delay;
    /* A new thermal zone needs to be updated anyway. */
    atomic_set(&tz->need_update, 1);

    dev_set_name(&tz->device, "thermal_zone%d", tz->id);
    result = device_register(&tz->device);--------------------------------创建/sys/devices/virtual/thermal/thermal_zone*设备。
    if (result) {
        release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
        kfree(tz);
        return ERR_PTR(result);
    }

    /* sys I/F */---------------------------------------------------------分别创建type、temp、mode、trip等sysfs节点。
    if (type) {
        result = device_create_file(&tz->device, &dev_attr_type);
        if (result)
            goto unregister;
    }
...
    result = create_trip_attrs(tz, mask);-----------------------为每个trip创建trip_point_*_temp/hyst/type节点。
    if (result)
        goto unregister;
...
/* Update 'this' zone's governor information */
    mutex_lock(&thermal_governor_lock);

    if (tz->tzp)-------------------------------------------------如果指定thermal zone的governor则通过__find_governor()选定;否则使用默认def_governor。
        governor = __find_governor(tz->tzp->governor_name);
    else
        governor = def_governor;

    result = thermal_set_governor(tz, governor);-----------------将governor绑定到tz上,优先使用bind_to_tz()执行绑定;否则直接指定tz->governor为governor。
    if (result) {
        mutex_unlock(&thermal_governor_lock);
        goto unregister;
    }

    mutex_unlock(&thermal_governor_lock);

    if (!tz->tzp || !tz->tzp->no_hwmon) {
        result = thermal_add_hwmon_sysfs(tz);
        if (result)
            goto unregister;
    }

    mutex_lock(&thermal_list_lock);
    list_add_tail(&tz->node, &thermal_tz_list);------------------------将当前thermal zone加入到thermal_tz_list列表上。
    mutex_unlock(&thermal_list_lock);

    /* Bind cooling devices for this zone */
    bind_tz(tz);-------------------------------------------------------调用tz->ops->bind()将thermal_cdev_list上的cooling设备绑定到tz上。

    INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);

    thermal_zone_device_reset(tz);-------------------------------------对thermal zone的温度等复位。
    /* Update the new thermal zone and mark it as already updated. */
    if (atomic_cmpxchg(&tz->need_update, 1, 0))
        thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);

    return tz;

unregister:
    release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
    device_unregister(&tz->device);
    return ERR_PTR(result);
}

static int thermal_set_governor(struct thermal_zone_device *tz,
                struct thermal_governor *new_gov)
{
    int ret = 0;

    if (tz->governor && tz->governor->unbind_from_tz)
        tz->governor->unbind_from_tz(tz);------------------------------先调用当前governor进行unbind()。

    if (new_gov && new_gov->bind_to_tz) {
        ret = new_gov->bind_to_tz(tz);---------------------------------使用当前governor进行bind()。
        if (ret) {
            bind_previous_governor(tz, new_gov->name);

            return ret;
        }
    }

    tz->governor = new_gov;--------------------------------------------更新tz->governor。

    return ret;
}

static void bind_tz(struct thermal_zone_device *tz)
{
    int i, ret;
    struct thermal_cooling_device *pos = NULL;
    const struct thermal_zone_params *tzp = tz->tzp;

    if (!tzp && !tz->ops->bind)
        return;

    mutex_lock(&thermal_list_lock);

    /* If there is ops->bind, try to use ops->bind */
    if (tz->ops->bind) {
        list_for_each_entry(pos, &thermal_cdev_list, node) {-----------遍历thermal_cdev_list的cooling设备,然后和当前thermal zone进行绑定。
            ret = tz->ops->bind(tz, pos);
            if (ret)
                print_bind_err_msg(tz, pos, ret);
        }
        goto exit;
    }
...
exit:
    mutex_unlock(&thermal_list_lock);
}

static void thermal_zone_device_check(struct work_struct *work)
{
    struct thermal_zone_device *tz = container_of(work, struct
                              thermal_zone_device,
                              poll_queue.work);
    thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}

 thermal_zone_device_unregister()则执行相反的操作,将thermal zone从thermal_tz_list上摘除,并且和cooling设备去绑定,以及删除一系列sysfs节点。

void thermal_zone_device_unregister(struct thermal_zone_device *tz)
{
    int i;
    const struct thermal_zone_params *tzp;
    struct thermal_cooling_device *cdev;
    struct thermal_zone_device *pos = NULL;

    if (!tz)
        return;

    tzp = tz->tzp;

    mutex_lock(&thermal_list_lock);
    list_for_each_entry(pos, &thermal_tz_list, node)
        if (pos == tz)
        break;
    if (pos != tz) {
        /* thermal zone device not found */
        mutex_unlock(&thermal_list_lock);
        return;
    }
    list_del(&tz->node);

    /* Unbind all cdevs associated with 'this' thermal zone */
    list_for_each_entry(cdev, &thermal_cdev_list, node) {
        if (tz->ops->unbind) {
            tz->ops->unbind(tz, cdev);
            continue;
        }
...
    }
...
    return;
}

thermal_cooling_device_register()创建cooling设备并放入thermal_cdev_list中,以及相关sysfs节点,并将cooling设备和thermal zone绑定。

thermal_cooling_device_unregister()则进行相反的操作。

struct thermal_cooling_device *
thermal_cooling_device_register(char *type, void *devdata,
                const struct thermal_cooling_device_ops *ops)
{
    return __thermal_cooling_device_register(NULL, type, devdata, ops);
}

static struct thermal_cooling_device *
__thermal_cooling_device_register(struct device_node *np,
                  char *type, void *devdata,
                  const struct thermal_cooling_device_ops *ops)
{
    struct thermal_cooling_device *cdev;
    struct thermal_zone_device *pos = NULL;
    int result;

    if (type && strlen(type) >= THERMAL_NAME_LENGTH)
        return ERR_PTR(-EINVAL);

    if (!ops || !ops->get_max_state || !ops->get_cur_state ||
        !ops->set_cur_state)
        return ERR_PTR(-EINVAL);

    cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
    if (!cdev)
        return ERR_PTR(-ENOMEM);

    result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
    if (result) {
        kfree(cdev);
        return ERR_PTR(result);
    }

    strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
    mutex_init(&cdev->lock);
    INIT_LIST_HEAD(&cdev->thermal_instances);
    cdev->np = np;
    cdev->ops = ops;
    cdev->updated = false;
    cdev->device.class = &thermal_class;---------------------------------cooling设备同样会在/sys/class/thermal下创建链接。
    cdev->device.groups = cooling_device_attr_groups;--------------------创建cur_state、max_state、type三个sysfs节点。
    cdev->devdata = devdata;
    dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
    result = device_register(&cdev->device);-----------------------------创建/sys/devices/virtual/thermal/cooling_device*设备节点。
    if (result) {
        release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
        kfree(cdev);
        return ERR_PTR(result);
    }

    /* Add 'this' new cdev to the global cdev list */
    mutex_lock(&thermal_list_lock);
    list_add(&cdev->node, &thermal_cdev_list);---------------------------将设备放入thermal_cdev_list设备链表。
    mutex_unlock(&thermal_list_lock);

    /* Update binding information for 'this' new cdev */
    bind_cdev(cdev);-----------------------------------------------------遍历thermal_tz_list,将cdev绑定到上面的thermal zone。

    mutex_lock(&thermal_list_lock);
    list_for_each_entry(pos, &thermal_tz_list, node)
        if (atomic_cmpxchg(&pos->need_update, 1, 0))
            thermal_zone_device_update(pos,
                           THERMAL_EVENT_UNSPECIFIED);
    mutex_unlock(&thermal_list_lock);

    return cdev;
}

void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
{
    int i;
    const struct thermal_zone_params *tzp;
    struct thermal_zone_device *tz;
    struct thermal_cooling_device *pos = NULL;

    if (!cdev)
        return;

    mutex_lock(&thermal_list_lock);
    list_for_each_entry(pos, &thermal_cdev_list, node)
        if (pos == cdev)
        break;
    if (pos != cdev) {
        /* thermal cooling device not found */
        mutex_unlock(&thermal_list_lock);
        return;
    }
    list_del(&cdev->node);

    /* Unbind all thermal zones associated with 'this' cdev */
    list_for_each_entry(tz, &thermal_tz_list, node) {
        if (tz->ops->unbind) {
            tz->ops->unbind(tz, cdev);
            continue;
        }

        if (!tz->tzp || !tz->tzp->tbp)
            continue;

        tzp = tz->tzp;
        for (i = 0; i < tzp->num_tbps; i++) {
            if (tzp->tbp[i].cdev == cdev) {
                __unbind(tz, tzp->tbp[i].trip_mask, cdev);
                tzp->tbp[i].cdev = NULL;
            }
        }
    }

    mutex_unlock(&thermal_list_lock);

    if (cdev->type[0])
        device_remove_file(&cdev->device, &dev_attr_cdev_type);
    device_remove_file(&cdev->device, &dev_attr_max_state);
    device_remove_file(&cdev->device, &dev_attr_cur_state);

    release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
    device_unregister(&cdev->device);
    return;
}

thermal_register_governor()首先判断thermal_governor_list上是否有同名governor,然后更新thermal_tz_list上未指定governor的thermal zone。

thermal_unregister_governor()则相反,将governor和thermal zone调用unbind_from_tz()并置空;最后从thermal_go上摘除。

int thermal_register_governor(struct thermal_governor *governor)
{
    int err;
    const char *name;
    struct thermal_zone_device *pos;

    if (!governor)
        return -EINVAL;

    mutex_lock(&thermal_governor_lock);

    err = -EBUSY;
    if (__find_governor(governor->name) == NULL) {--------------------检查此governor是否已经在thermal_governor_list中,如果不在则加入thermal_governor_list。并且判断是否为def_governor。
        err = 0;
        list_add(&governor->governor_list, &thermal_governor_list);
        if (!def_governor && !strncmp(governor->name,
            DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
            def_governor = governor;
    }

    mutex_lock(&thermal_list_lock);

    list_for_each_entry(pos, &thermal_tz_list, node) {
        if (pos->governor)--------------------------------------------如果thermal zone已经制定governor,则跳过。
            continue;
        name = pos->tzp->governor_name;
        if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
            int ret;

            ret = thermal_set_governor(pos, governor);----------------给当前thermal zone制定governor。
            if (ret)
                dev_err(&pos->device,
                    "Failed to set governor %s for thermal zone %s: %d\n",
                    governor->name, pos->type, ret);
        }
    }

    mutex_unlock(&thermal_list_lock);
    mutex_unlock(&thermal_governor_lock);

    return err;
}

void thermal_unregister_governor(struct thermal_governor *governor)
{
    struct thermal_zone_device *pos;

    if (!governor)
        return;

    mutex_lock(&thermal_governor_lock);

    if (__find_governor(governor->name) == NULL)
        goto exit;

    mutex_lock(&thermal_list_lock);

    list_for_each_entry(pos, &thermal_tz_list, node) {
        if (!strncasecmp(pos->governor->name, governor->name,
                        THERMAL_NAME_LENGTH))
            thermal_set_governor(pos, NULL);
    }

    mutex_unlock(&thermal_list_lock);
    list_del(&governor->governor_list);
exit:
    mutex_unlock(&thermal_governor_lock);
    return;
}

thermal_zone_bind_cooling_device()通过创建thermal_instances设备将Thermal Zone和Thermal Cooling绑定,这样Thermal Zone就可以根据温度处理Thermal Cooling设备。

thermal_zone_unbind_cooling_device() 则将关联Thermal Zone和Thermal Cooling的thermal_instances从两者的链表上摘除。

int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
                     int trip,
                     struct thermal_cooling_device *cdev,
                     unsigned long upper, unsigned long lower,
                     unsigned int weight)
{
    struct thermal_instance *dev;
    struct thermal_instance *pos;
    struct thermal_zone_device *pos1;
    struct thermal_cooling_device *pos2;
    unsigned long max_state;
    int result, ret;

    if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
        return -EINVAL;

    list_for_each_entry(pos1, &thermal_tz_list, node) {
        if (pos1 == tz)
            break;
    }
    list_for_each_entry(pos2, &thermal_cdev_list, node) {
        if (pos2 == cdev)
            break;
    }

    if (tz != pos1 || cdev != pos2)
        return -EINVAL;

    ret = cdev->ops->get_max_state(cdev, &max_state);----------------------从Cooling设备操作函数get_max_state()获取max_state,进而决定thermal_instances的lower和upper范围。
    if (ret)
        return ret;

    /* lower default 0, upper default max_state */
    lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
    upper = upper == THERMAL_NO_LIMIT ? max_state : upper;

if (lower > upper || upper > max_state)
        return -EINVAL;

    dev =
        kzalloc(sizeof(struct thermal_instance), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
    dev->tz = tz;
    dev->cdev = cdev;
    dev->trip = trip;
    dev->upper = upper;
    dev->lower = lower;
    dev->target = THERMAL_NO_TARGET;
    dev->weight = weight;

    result = get_idr(&tz->idr, &tz->lock, &dev->id);
    
																				
															

推荐阅读