• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
get_hv_confstr(char * page,int query)25 static 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 
chip_width_show(struct device * dev,struct device_attribute * attr,char * page)35 static 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 }
41 static DEVICE_ATTR(chip_width, 0444, chip_width_show, NULL);
42 
chip_height_show(struct device * dev,struct device_attribute * attr,char * page)43 static 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 }
49 static DEVICE_ATTR(chip_height, 0444, chip_height_show, NULL);
50 
chip_serial_show(struct device * dev,struct device_attribute * attr,char * page)51 static 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 }
57 static DEVICE_ATTR(chip_serial, 0444, chip_serial_show, NULL);
58 
chip_revision_show(struct device * dev,struct device_attribute * attr,char * page)59 static 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 }
65 static DEVICE_ATTR(chip_revision, 0444, chip_revision_show, NULL);
66 
67 
type_show(struct device * dev,struct device_attribute * attr,char * page)68 static ssize_t type_show(struct device *dev,
69 			    struct device_attribute *attr,
70 			    char *page)
71 {
72 	return sprintf(page, "tilera\n");
73 }
74 static 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 
85 HV_CONF_ATTR(version,		HV_CONFSTR_HV_SW_VER)
86 HV_CONF_ATTR(config_version,	HV_CONFSTR_HV_CONFIG_VER)
87 
88 HV_CONF_ATTR(board_part,	HV_CONFSTR_BOARD_PART_NUM)
89 HV_CONF_ATTR(board_serial,	HV_CONFSTR_BOARD_SERIAL_NUM)
90 HV_CONF_ATTR(board_revision,	HV_CONFSTR_BOARD_REV)
91 HV_CONF_ATTR(board_description,	HV_CONFSTR_BOARD_DESC)
92 HV_CONF_ATTR(mezz_part,		HV_CONFSTR_MEZZ_PART_NUM)
93 HV_CONF_ATTR(mezz_serial,	HV_CONFSTR_MEZZ_SERIAL_NUM)
94 HV_CONF_ATTR(mezz_revision,	HV_CONFSTR_MEZZ_REV)
95 HV_CONF_ATTR(mezz_description,	HV_CONFSTR_MEZZ_DESC)
96 HV_CONF_ATTR(cpumod_part,	HV_CONFSTR_CPUMOD_PART_NUM)
97 HV_CONF_ATTR(cpumod_serial,	HV_CONFSTR_CPUMOD_SERIAL_NUM)
98 HV_CONF_ATTR(cpumod_revision,	HV_CONFSTR_CPUMOD_REV)
99 HV_CONF_ATTR(cpumod_description,HV_CONFSTR_CPUMOD_DESC)
100 HV_CONF_ATTR(switch_control,	HV_CONFSTR_SWITCH_CONTROL)
101 
102 static 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 
119 static struct attribute_group board_attr_group = {
120 	.name   = "board",
121 	.attrs  = board_attrs,
122 };
123 
124 
125 static struct bin_attribute hvconfig_bin;
126 
127 static ssize_t
hvconfig_bin_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)128 hvconfig_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 
hv_stats_show(struct device * dev,struct device_attribute * attr,char * page)160 static 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 
hv_stats_store(struct device * dev,struct device_attribute * attr,const char * page,size_t count)175 static 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 
187 static DEVICE_ATTR(hv_stats, 0644, hv_stats_show, hv_stats_store);
188 
hv_stats_device_add(struct device * dev,struct subsys_interface * sif)189 static 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 
hv_stats_device_remove(struct device * dev,struct subsys_interface * sif)201 static int 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 		return 0;
208 
209 	sysfs_remove_file(&dev->kobj, &dev_attr_hv_stats.attr);
210 	return 0;
211 }
212 
213 
214 static struct subsys_interface hv_stats_interface = {
215 	.name			= "hv_stats",
216 	.subsys			= &cpu_subsys,
217 	.add_dev		= hv_stats_device_add,
218 	.remove_dev		= hv_stats_device_remove,
219 };
220 
create_sysfs_entries(void)221 static int __init create_sysfs_entries(void)
222 {
223 	int err = 0;
224 
225 #define create_cpu_attr(name)						\
226 	if (!err)							\
227 		err = device_create_file(cpu_subsys.dev_root, &dev_attr_##name);
228 	create_cpu_attr(chip_width);
229 	create_cpu_attr(chip_height);
230 	create_cpu_attr(chip_serial);
231 	create_cpu_attr(chip_revision);
232 
233 #define create_hv_attr(name)						\
234 	if (!err)							\
235 		err = sysfs_create_file(hypervisor_kobj, &dev_attr_##name.attr);
236 	create_hv_attr(type);
237 	create_hv_attr(version);
238 	create_hv_attr(config_version);
239 
240 	if (!err)
241 		err = sysfs_create_group(hypervisor_kobj, &board_attr_group);
242 
243 	if (!err) {
244 		sysfs_bin_attr_init(&hvconfig_bin);
245 		hvconfig_bin.attr.name = "hvconfig";
246 		hvconfig_bin.attr.mode = S_IRUGO;
247 		hvconfig_bin.read = hvconfig_bin_read;
248 		hvconfig_bin.size = PAGE_SIZE;
249 		err = sysfs_create_bin_file(hypervisor_kobj, &hvconfig_bin);
250 	}
251 
252 	if (!err) {
253 		/*
254 		 * Don't bother adding the hv_stats files on each CPU if
255 		 * our hypervisor doesn't supply statistics.
256 		 */
257 		int cpu = raw_smp_processor_id();
258 		long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));
259 		char dummy;
260 		ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS,
261 				       (unsigned long) &dummy, 1,
262 				       lotar, 0);
263 		if (n >= 0)
264 			err = subsys_interface_register(&hv_stats_interface);
265 	}
266 
267 	return err;
268 }
269 subsys_initcall(create_sysfs_entries);
270