/* * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "edp_internal.h" static struct kobject edp_kobj; struct manager_entry { struct edp_manager *manager; struct kobject kobj; }; struct manager_attr { struct attribute attr; ssize_t (*show)(struct edp_manager *, char *); ssize_t (*store)(struct edp_manager *, const char *, size_t); }; static ssize_t cap_show(struct edp_manager *m, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", m->max); } static ssize_t remaining_show(struct edp_manager *m, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", m->remaining); } static ssize_t denied_show(struct edp_manager *m, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", m->num_denied); } static ssize_t manager_governor_show(struct edp_manager *m, char *s) { return scnprintf(s, PAGE_SIZE, "%s\n", m->gov ? m->gov->name : ""); } static ssize_t manager_governor_store(struct edp_manager *m, const char *s, size_t count) { char name[EDP_NAME_LEN]; struct edp_governor *gov; if (!count || count >= sizeof(name)) return -EINVAL; memcpy(name, s, count); name[count] = 0; strim(name); gov = edp_find_governor_unlocked(name); if (!gov) return -EINVAL; return edp_set_governor_unlocked(m, gov) ?: count; } struct manager_attr attr_cap = __ATTR_RO(cap); struct manager_attr attr_remaining = __ATTR_RO(remaining); struct manager_attr attr_denied = __ATTR_RO(denied); struct manager_attr attr_mgr_gov = __ATTR(governor, 0644, manager_governor_show, manager_governor_store); static struct attribute *manager_attrs[] = { &attr_cap.attr, &attr_remaining.attr, &attr_denied.attr, &attr_mgr_gov.attr, NULL }; static struct edp_manager *to_manager(struct kobject *kobj) { struct manager_entry *me = container_of(kobj, struct manager_entry, kobj); return me ? me->manager : NULL; } static ssize_t manager_state_show(struct kobject *kobj, struct attribute *attr, char *buf) { ssize_t r; struct edp_manager *m; struct manager_attr *mattr; mutex_lock(&edp_lock); m = to_manager(kobj); mattr = container_of(attr, struct manager_attr, attr); r = m && mattr ? mattr->show(m, buf) : -EINVAL; mutex_unlock(&edp_lock); return r; } static ssize_t manager_state_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { ssize_t r; struct edp_manager *m; struct manager_attr *mattr; mutex_lock(&edp_lock); m = to_manager(kobj); mattr = container_of(attr, struct manager_attr, attr); r = m && mattr ? mattr->store(m, buf, count) : -EINVAL; mutex_unlock(&edp_lock); return r; } static const struct sysfs_ops manager_sysfs_ops = { .show = manager_state_show, .store = manager_state_store }; static struct kobj_type ktype_manager = { .sysfs_ops = &manager_sysfs_ops, .default_attrs = manager_attrs }; void edp_manager_add_kobject(struct edp_manager *mgr) { struct manager_entry *me; me = kzalloc(sizeof(*me), GFP_KERNEL); if (!me) { pr_err("%s: failed to alloc sysfs manager entry\n", mgr->name); return; } if (kobject_init_and_add(&me->kobj, &ktype_manager, &edp_kobj, mgr->name)) { pr_err("%s: failed to init & add sysfs manager entry\n", mgr->name); kfree(me); return; } me->manager = mgr; mgr->kobj = &me->kobj; kobject_uevent(&me->kobj, KOBJ_ADD); return; } void edp_manager_remove_kobject(struct edp_manager *mgr) { struct manager_entry *me; if (!mgr->kobj) return; me = container_of(mgr->kobj, struct manager_entry, kobj); mgr->kobj = NULL; kobject_put(&me->kobj); kfree(me); } struct client_entry { struct edp_client *client; struct kobject kobj; }; static ssize_t states_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { unsigned int i; int cnt = 0; const int sz = sizeof(*c->states) * 3 + 2; for (i = 0; i < c->num_states && (cnt + sz) < PAGE_SIZE; i++) cnt += sprintf(s + cnt, "%s%u", i ? " " : "", c->states[i]); cnt += sprintf(s + cnt, "\n"); return cnt; } static ssize_t num_states_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->num_states); } static ssize_t e0_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->states[c->e0_index]); } static ssize_t max_borrowers_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->max_borrowers); } static ssize_t priority_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%d\n", c->priority); } static ssize_t request_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", req_level(c)); } /* Allow only updates that are guaranteed to succeed */ static ssize_t request_store(struct edp_client *c, struct edp_client_attribute *attr, const char *s, size_t count) { unsigned int id; int r; if (sscanf(s, "%u", &id) != 1) return -EINVAL; mutex_lock(&edp_lock); if (id >= c->num_states) { r = -EINVAL; goto out; } if (id < c->e0_index && id < req_index(c)) { r = -EPERM; goto out; } r = edp_update_client_request_unlocked(c, id, NULL); out: mutex_unlock(&edp_lock); return r ?: count; } static ssize_t current_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", cur_level(c)); } static ssize_t threshold_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->ithreshold); } static ssize_t threshold_store(struct edp_client *c, struct edp_client_attribute *attr, const char *s, size_t count) { unsigned int tv; int r; if (sscanf(s, "%u", &tv) != 1) return -EINVAL; r = edp_update_loan_threshold(c, tv); return r ?: count; } static ssize_t borrowers_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->num_borrowers); } static ssize_t loans_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->num_loans); } static ssize_t notify_show(struct edp_client *c, struct edp_client_attribute *attr, char *s) { return scnprintf(s, PAGE_SIZE, "%u\n", c->notify_ui); } static ssize_t notify_store(struct edp_client *c, struct edp_client_attribute *attr, const char *s, size_t count) { unsigned int f; if (sscanf(s, "%u", &f) != 1) return -EINVAL; c->notify_ui = f; return count; } struct edp_client_attribute attr_states = __ATTR_RO(states); struct edp_client_attribute attr_num_states = __ATTR_RO(num_states); struct edp_client_attribute attr_e0 = __ATTR_RO(e0); struct edp_client_attribute attr_max_borrowers = __ATTR_RO(max_borrowers); struct edp_client_attribute attr_priority = __ATTR_RO(priority); struct edp_client_attribute attr_request = __ATTR(request, 0644, request_show, request_store); struct edp_client_attribute attr_threshold = __ATTR(threshold, 0644, threshold_show, threshold_store); struct edp_client_attribute attr_borrowers = __ATTR_RO(borrowers); struct edp_client_attribute attr_loans = __ATTR_RO(loans); struct edp_client_attribute attr_current = { .attr = { .name = "current", .mode = 0444 }, .show = current_show }; struct edp_client_attribute attr_notify = __ATTR(notify, 0644, notify_show, notify_store); static struct attribute *client_attrs[] = { &attr_states.attr, &attr_num_states.attr, &attr_e0.attr, &attr_max_borrowers.attr, &attr_priority.attr, &attr_request.attr, &attr_current.attr, &attr_threshold.attr, &attr_borrowers.attr, &attr_loans.attr, &attr_notify.attr, NULL }; static struct edp_client *to_client(struct kobject *kobj) { struct client_entry *ce = container_of(kobj, struct client_entry, kobj); return ce ? ce->client : NULL; } static ssize_t client_state_show(struct kobject *kobj, struct attribute *attr, char *buf) { ssize_t r = -EINVAL; struct edp_client *c; struct edp_client_attribute *cattr; c = to_client(kobj); cattr = container_of(attr, struct edp_client_attribute, attr); if (c && cattr) { if (cattr->show) r = cattr->show(c, cattr, buf); } return r; } static ssize_t client_state_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { ssize_t r = -EINVAL; struct edp_client *c; struct edp_client_attribute *cattr; c = to_client(kobj); cattr = container_of(attr, struct edp_client_attribute, attr); if (c && cattr) { if (cattr->store) r = cattr->store(c, cattr, buf, count); } return r; } static const struct sysfs_ops client_sysfs_ops = { .show = client_state_show, .store = client_state_store }; static struct kobj_type ktype_client = { .sysfs_ops = &client_sysfs_ops, .default_attrs = client_attrs }; static void create_driver_attrs(struct edp_client *c) { struct edp_client_attribute *p; int r; if (!c->attrs) return; for (p = c->attrs; attr_name(*p); p++) { r = sysfs_create_file(c->kobj, &p->attr); WARN_ON(r); } } static void remove_driver_attrs(struct edp_client *c) { struct edp_client_attribute *p; if (!c->attrs) return; for (p = c->attrs; attr_name(*p); p++) sysfs_remove_file(c->kobj, &p->attr); } void edp_client_add_kobject(struct edp_client *client) { struct client_entry *ce; struct kobject *parent = client->manager->kobj; if (!parent) return; ce = kzalloc(sizeof(*ce), GFP_KERNEL); if (!ce) { pr_err("%s: failed to alloc sysfs client entry\n", client->name); return; } if (kobject_init_and_add(&ce->kobj, &ktype_client, parent, client->name)) { pr_err("%s: failed to init & add sysfs client entry\n", client->name); kfree(ce); return; } ce->client = client; client->kobj = &ce->kobj; create_driver_attrs(client); kobject_uevent(&ce->kobj, KOBJ_ADD); } void edp_client_remove_kobject(struct edp_client *client) { struct client_entry *ce; if (!client->kobj) return; ce = container_of(client->kobj, struct client_entry, kobj); remove_driver_attrs(client); client->kobj = NULL; kobject_put(&ce->kobj); kfree(ce); } static ssize_t governors_show(struct kobject *kobj, struct attribute *attr, char *s) { struct edp_governor *g; int cnt = 0; mutex_lock(&edp_lock); list_for_each_entry(g, &edp_governors, link) { if (cnt + EDP_NAME_LEN + 2 >= PAGE_SIZE) break; cnt += sprintf(s + cnt, "%s%s", cnt ? " " : "", g->name); } cnt += sprintf(s + cnt, "\n"); mutex_unlock(&edp_lock); return cnt; } static const struct sysfs_ops edp_sysfs_ops = { .show = governors_show }; static struct attribute attr_governors = { .name = "governors", .mode = 0444 }; static struct attribute *edp_attrs[] = { &attr_governors, NULL }; static struct kobj_type ktype_edp = { .sysfs_ops = &edp_sysfs_ops, .default_attrs = edp_attrs }; static int __init edp_sysfs_init(void) { struct kobject *parent = NULL; #ifdef CONFIG_PM parent = power_kobj; #endif return kobject_init_and_add(&edp_kobj, &ktype_edp, parent, "edp"); } postcore_initcall(edp_sysfs_init);