linux/arch/tile/kernel/sysfs.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 *
  14 * /sys entry support.
  15 */
  16
  17#include <linux/device.h>
  18#include <linux/cpu.h>
  19#include <linux/slab.h>
  20#include <linux/smp.h>
  21#include <linux/stat.h>
  22#include <hv/hypervisor.h>
  23
  24/* Return a string queried from the hypervisor, truncated to page size. */
  25static ssize_t get_hv_confstr(char *page, int query)
  26{
  27        ssize_t n = hv_confstr(query, (unsigned long)page, PAGE_SIZE - 1);
  28        n = n < 0 ? 0 : min(n, (ssize_t)PAGE_SIZE - 1) - 1;
  29        if (n)
  30                page[n++] = '\n';
  31        page[n] = '\0';
  32        return n;
  33}
  34
  35static ssize_t chip_width_show(struct device *dev,
  36                               struct device_attribute *attr,
  37                               char *page)
  38{
  39        return sprintf(page, "%u\n", smp_width);
  40}
  41static DEVICE_ATTR(chip_width, 0444, chip_width_show, NULL);
  42
  43static ssize_t chip_height_show(struct device *dev,
  44                                struct device_attribute *attr,
  45                                char *page)
  46{
  47        return sprintf(page, "%u\n", smp_height);
  48}
  49static DEVICE_ATTR(chip_height, 0444, chip_height_show, NULL);
  50
  51static ssize_t chip_serial_show(struct device *dev,
  52                                struct device_attribute *attr,
  53                                char *page)
  54{
  55        return get_hv_confstr(page, HV_CONFSTR_CHIP_SERIAL_NUM);
  56}
  57static DEVICE_ATTR(chip_serial, 0444, chip_serial_show, NULL);
  58
  59static ssize_t chip_revision_show(struct device *dev,
  60                                  struct device_attribute *attr,
  61                                  char *page)
  62{
  63        return get_hv_confstr(page, HV_CONFSTR_CHIP_REV);
  64}
  65static DEVICE_ATTR(chip_revision, 0444, chip_revision_show, NULL);
  66
  67
  68static ssize_t type_show(struct device *dev,
  69                            struct device_attribute *attr,
  70                            char *page)
  71{
  72        return sprintf(page, "tilera\n");
  73}
  74static DEVICE_ATTR(type, 0444, type_show, NULL);
  75
  76#define HV_CONF_ATTR(name, conf)                                        \
  77        static ssize_t name ## _show(struct device *dev,                \
  78                                     struct device_attribute *attr, \
  79                                     char *page)                        \
  80        {                                                               \
  81                return get_hv_confstr(page, conf);                      \
  82        }                                                               \
  83        static DEVICE_ATTR(name, 0444, name ## _show, NULL);
  84
  85HV_CONF_ATTR(version,           HV_CONFSTR_HV_SW_VER)
  86HV_CONF_ATTR(config_version,    HV_CONFSTR_HV_CONFIG_VER)
  87
  88HV_CONF_ATTR(board_part,        HV_CONFSTR_BOARD_PART_NUM)
  89HV_CONF_ATTR(board_serial,      HV_CONFSTR_BOARD_SERIAL_NUM)
  90HV_CONF_ATTR(board_revision,    HV_CONFSTR_BOARD_REV)
  91HV_CONF_ATTR(board_description, HV_CONFSTR_BOARD_DESC)
  92HV_CONF_ATTR(mezz_part,         HV_CONFSTR_MEZZ_PART_NUM)
  93HV_CONF_ATTR(mezz_serial,       HV_CONFSTR_MEZZ_SERIAL_NUM)
  94HV_CONF_ATTR(mezz_revision,     HV_CONFSTR_MEZZ_REV)
  95HV_CONF_ATTR(mezz_description,  HV_CONFSTR_MEZZ_DESC)
  96HV_CONF_ATTR(cpumod_part,       HV_CONFSTR_CPUMOD_PART_NUM)
  97HV_CONF_ATTR(cpumod_serial,     HV_CONFSTR_CPUMOD_SERIAL_NUM)
  98HV_CONF_ATTR(cpumod_revision,   HV_CONFSTR_CPUMOD_REV)
  99HV_CONF_ATTR(cpumod_description,HV_CONFSTR_CPUMOD_DESC)
 100HV_CONF_ATTR(switch_control,    HV_CONFSTR_SWITCH_CONTROL)
 101
 102static struct attribute *board_attrs[] = {
 103        &dev_attr_board_part.attr,
 104        &dev_attr_board_serial.attr,
 105        &dev_attr_board_revision.attr,
 106        &dev_attr_board_description.attr,
 107        &dev_attr_mezz_part.attr,
 108        &dev_attr_mezz_serial.attr,
 109        &dev_attr_mezz_revision.attr,
 110        &dev_attr_mezz_description.attr,
 111        &dev_attr_cpumod_part.attr,
 112        &dev_attr_cpumod_serial.attr,
 113        &dev_attr_cpumod_revision.attr,
 114        &dev_attr_cpumod_description.attr,
 115        &dev_attr_switch_control.attr,
 116        NULL
 117};
 118
 119static struct attribute_group board_attr_group = {
 120        .name   = "board",
 121        .attrs  = board_attrs,
 122};
 123
 124
 125static struct bin_attribute hvconfig_bin;
 126
 127static ssize_t
 128hvconfig_bin_read(struct file *filp, struct kobject *kobj,
 129                  struct bin_attribute *bin_attr,
 130                  char *buf, loff_t off, size_t count)
 131{
 132        static size_t size;
 133
 134        /* Lazily learn the true size (minus the trailing NUL). */
 135        if (size == 0)
 136                size = hv_confstr(HV_CONFSTR_HV_CONFIG, 0, 0) - 1;
 137
 138        /* Check and adjust input parameters. */
 139        if (off > size)
 140                return -EINVAL;
 141        if (count > size - off)
 142                count = size - off;
 143
 144        if (count) {
 145                /* Get a copy of the hvc and copy out the relevant portion. */
 146                char *hvc;
 147
 148                size = off + count;
 149                hvc = kmalloc(size, GFP_KERNEL);
 150                if (hvc == NULL)
 151                        return -ENOMEM;
 152                hv_confstr(HV_CONFSTR_HV_CONFIG, (unsigned long)hvc, size);
 153                memcpy(buf, hvc + off, count);
 154                kfree(hvc);
 155        }
 156
 157        return count;
 158}
 159
 160static ssize_t hv_stats_show(struct device *dev,
 161                             struct device_attribute *attr,
 162                             char *page)
 163{
 164        int cpu = dev->id;
 165        long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));
 166
 167        ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS,
 168                               (unsigned long)page, PAGE_SIZE - 1,
 169                               lotar, 0);
 170        n = n < 0 ? 0 : min(n, (ssize_t)PAGE_SIZE - 1);
 171        page[n] = '\0';
 172        return n;
 173}
 174
 175static ssize_t hv_stats_store(struct device *dev,
 176                              struct device_attribute *attr,
 177                              const char *page,
 178                              size_t count)
 179{
 180        int cpu = dev->id;
 181        long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));
 182
 183        ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS, 0, 0, lotar, 1);
 184        return n < 0 ? n : count;
 185}
 186
 187static DEVICE_ATTR(hv_stats, 0644, hv_stats_show, hv_stats_store);
 188
 189static int hv_stats_device_add(struct device *dev, struct subsys_interface *sif)
 190{
 191        int err, cpu = dev->id;
 192
 193        if (!cpu_online(cpu))
 194                return 0;
 195
 196        err = sysfs_create_file(&dev->kobj, &dev_attr_hv_stats.attr);
 197
 198        return err;
 199}
 200
 201static void hv_stats_device_remove(struct device *dev,
 202                                   struct subsys_interface *sif)
 203{
 204        int cpu = dev->id;
 205
 206        if (cpu_online(cpu))
 207                sysfs_remove_file(&dev->kobj, &dev_attr_hv_stats.attr);
 208}
 209
 210
 211static struct subsys_interface hv_stats_interface = {
 212        .name                   = "hv_stats",
 213        .subsys                 = &cpu_subsys,
 214        .add_dev                = hv_stats_device_add,
 215        .remove_dev             = hv_stats_device_remove,
 216};
 217
 218static int __init create_sysfs_entries(void)
 219{
 220        int err = 0;
 221
 222#define create_cpu_attr(name)                                           \
 223        if (!err)                                                       \
 224                err = device_create_file(cpu_subsys.dev_root, &dev_attr_##name);
 225        create_cpu_attr(chip_width);
 226        create_cpu_attr(chip_height);
 227        create_cpu_attr(chip_serial);
 228        create_cpu_attr(chip_revision);
 229
 230#define create_hv_attr(name)                                            \
 231        if (!err)                                                       \
 232                err = sysfs_create_file(hypervisor_kobj, &dev_attr_##name.attr);
 233        create_hv_attr(type);
 234        create_hv_attr(version);
 235        create_hv_attr(config_version);
 236
 237        if (!err)
 238                err = sysfs_create_group(hypervisor_kobj, &board_attr_group);
 239
 240        if (!err) {
 241                sysfs_bin_attr_init(&hvconfig_bin);
 242                hvconfig_bin.attr.name = "hvconfig";
 243                hvconfig_bin.attr.mode = S_IRUGO;
 244                hvconfig_bin.read = hvconfig_bin_read;
 245                hvconfig_bin.size = PAGE_SIZE;
 246                err = sysfs_create_bin_file(hypervisor_kobj, &hvconfig_bin);
 247        }
 248
 249        if (!err) {
 250                /*
 251                 * Don't bother adding the hv_stats files on each CPU if
 252                 * our hypervisor doesn't supply statistics.
 253                 */
 254                int cpu = raw_smp_processor_id();
 255                long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));
 256                char dummy;
 257                ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS,
 258                                       (unsigned long) &dummy, 1,
 259                                       lotar, 0);
 260                if (n >= 0)
 261                        err = subsys_interface_register(&hv_stats_interface);
 262        }
 263
 264        return err;
 265}
 266subsys_initcall(create_sysfs_entries);
 267