blob: 2af4ee67fe52a1bbdc4228dc1d0f034cba651368 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Heiko Carstens94f5b092009-03-26 15:23:50 +01002 * Copyright IBM Corp. 2001, 2009
3 * Author(s): Ulrich Weigand <Ulrich.Weigand@de.ibm.com>,
4 * Martin Schwidefsky <schwidefsky@de.ibm.com>,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 */
6
Linus Torvalds1da177e2005-04-16 15:20:36 -07007#include <linux/kernel.h>
8#include <linux/mm.h>
9#include <linux/proc_fs.h>
Martin Schwidefsky6bcac502008-12-25 13:38:49 +010010#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/init.h>
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +010012#include <linux/delay.h>
Martin Schwidefsky6bcac502008-12-25 13:38:49 +010013#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090014#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <asm/ebcdic.h>
Christian Borntraegeraa24f7f2008-04-17 07:46:09 +020016#include <asm/sysinfo.h>
Martin Schwidefsky6bcac502008-12-25 13:38:49 +010017#include <asm/cpcmd.h>
Heiko Carstens96f4a702010-10-25 16:10:54 +020018#include <asm/topology.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +010020/* Sigh, math-emu. Don't ask. */
21#include <asm/sfp-util.h>
22#include <math-emu/soft-fp.h>
23#include <math-emu/single.h>
24
Heiko Carstensfade4dc2012-09-04 14:26:03 +020025int topology_max_mnest;
26
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020027static inline int stsi_0(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
Heiko Carstens94f5b092009-03-26 15:23:50 +010029 int rc = stsi(NULL, 0, 0, 0);
Heiko Carstens0facaa12012-09-03 09:38:30 +020030
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020031 return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -070032}
33
Heiko Carstens0facaa12012-09-03 09:38:30 +020034static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -070035{
Heiko Carstens25502f02012-09-03 14:05:05 +020036 int i;
37
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020038 if (stsi(info, 1, 1, 1) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +020039 return;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020040 EBCASC(info->manufacturer, sizeof(info->manufacturer));
41 EBCASC(info->type, sizeof(info->type));
42 EBCASC(info->model, sizeof(info->model));
43 EBCASC(info->sequence, sizeof(info->sequence));
44 EBCASC(info->plant, sizeof(info->plant));
45 EBCASC(info->model_capacity, sizeof(info->model_capacity));
Martin Schwidefskycbce70e2008-04-17 07:46:10 +020046 EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
47 EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
Heiko Carstens0facaa12012-09-03 09:38:30 +020048 seq_printf(m, "Manufacturer: %-16.16s\n", info->manufacturer);
49 seq_printf(m, "Type: %-4.4s\n", info->type);
50 /*
51 * Sigh: the model field has been renamed with System z9
52 * to model_capacity and a new model field has been added
53 * after the plant field. To avoid confusing older programs
54 * the "Model:" prints "model_capacity model" or just
55 * "model_capacity" if the model string is empty .
56 */
57 seq_printf(m, "Model: %-16.16s", info->model_capacity);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020058 if (info->model[0] != '\0')
Heiko Carstens0facaa12012-09-03 09:38:30 +020059 seq_printf(m, " %-16.16s", info->model);
60 seq_putc(m, '\n');
61 seq_printf(m, "Sequence Code: %-16.16s\n", info->sequence);
62 seq_printf(m, "Plant: %-4.4s\n", info->plant);
63 seq_printf(m, "Model Capacity: %-16.16s %08u\n",
Heiko Carstens25502f02012-09-03 14:05:05 +020064 info->model_capacity, info->model_cap_rating);
65 if (info->model_perm_cap_rating)
Heiko Carstens0facaa12012-09-03 09:38:30 +020066 seq_printf(m, "Model Perm. Capacity: %-16.16s %08u\n",
67 info->model_perm_cap,
Heiko Carstens25502f02012-09-03 14:05:05 +020068 info->model_perm_cap_rating);
69 if (info->model_temp_cap_rating)
Heiko Carstens0facaa12012-09-03 09:38:30 +020070 seq_printf(m, "Model Temp. Capacity: %-16.16s %08u\n",
71 info->model_temp_cap,
Heiko Carstens25502f02012-09-03 14:05:05 +020072 info->model_temp_cap_rating);
73 if (info->ncr)
74 seq_printf(m, "Nominal Cap. Rating: %08u\n", info->ncr);
75 if (info->npr)
76 seq_printf(m, "Nominal Perm. Rating: %08u\n", info->npr);
77 if (info->ntr)
78 seq_printf(m, "Nominal Temp. Rating: %08u\n", info->ntr);
Heiko Carstens7aca2ed2010-10-25 16:10:16 +020079 if (info->cai) {
Heiko Carstens0facaa12012-09-03 09:38:30 +020080 seq_printf(m, "Capacity Adj. Ind.: %d\n", info->cai);
81 seq_printf(m, "Capacity Ch. Reason: %d\n", info->ccr);
Heiko Carstens25502f02012-09-03 14:05:05 +020082 seq_printf(m, "Capacity Transient: %d\n", info->t);
83 }
84 if (info->p) {
85 for (i = 1; i <= ARRAY_SIZE(info->typepct); i++) {
86 seq_printf(m, "Type %d Percentage: %d\n",
87 i, info->typepct[i - 1]);
88 }
Heiko Carstens7aca2ed2010-10-25 16:10:16 +020089 }
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020090}
91
Heiko Carstens0facaa12012-09-03 09:38:30 +020092static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)
Heiko Carstens96f4a702010-10-25 16:10:54 +020093{
94 static int max_mnest;
95 int i, rc;
96
Heiko Carstens0facaa12012-09-03 09:38:30 +020097 seq_putc(m, '\n');
Heiko Carstens96f4a702010-10-25 16:10:54 +020098 if (!MACHINE_HAS_TOPOLOGY)
Heiko Carstens0facaa12012-09-03 09:38:30 +020099 return;
Heiko Carstensfade4dc2012-09-04 14:26:03 +0200100 stsi(info, 15, 1, topology_max_mnest);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200101 seq_printf(m, "CPU Topology HW: ");
Heiko Carstens96f4a702010-10-25 16:10:54 +0200102 for (i = 0; i < TOPOLOGY_NR_MAG; i++)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200103 seq_printf(m, " %d", info->mag[i]);
104 seq_putc(m, '\n');
Heiko Carstens8d11e022010-10-29 16:50:37 +0200105#ifdef CONFIG_SCHED_MC
Heiko Carstens96f4a702010-10-25 16:10:54 +0200106 store_topology(info);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200107 seq_printf(m, "CPU Topology SW: ");
Heiko Carstens96f4a702010-10-25 16:10:54 +0200108 for (i = 0; i < TOPOLOGY_NR_MAG; i++)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200109 seq_printf(m, " %d", info->mag[i]);
110 seq_putc(m, '\n');
Heiko Carstens8d11e022010-10-29 16:50:37 +0200111#endif
Heiko Carstens96f4a702010-10-25 16:10:54 +0200112}
113
Heiko Carstens0facaa12012-09-03 09:38:30 +0200114static void stsi_1_2_2(struct seq_file *m, struct sysinfo_1_2_2 *info)
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200115{
116 struct sysinfo_1_2_2_extension *ext;
117 int i;
118
119 if (stsi(info, 1, 2, 2) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200120 return;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200121 ext = (struct sysinfo_1_2_2_extension *)
122 ((unsigned long) info + info->acc_offset);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200123 seq_printf(m, "CPUs Total: %d\n", info->cpus_total);
124 seq_printf(m, "CPUs Configured: %d\n", info->cpus_configured);
125 seq_printf(m, "CPUs Standby: %d\n", info->cpus_standby);
126 seq_printf(m, "CPUs Reserved: %d\n", info->cpus_reserved);
127 /*
128 * Sigh 2. According to the specification the alternate
129 * capability field is a 32 bit floating point number
130 * if the higher order 8 bits are not zero. Printing
131 * a floating point number in the kernel is a no-no,
132 * always print the number as 32 bit unsigned integer.
133 * The user-space needs to know about the strange
134 * encoding of the alternate cpu capability.
135 */
136 seq_printf(m, "Capability: %u", info->capability);
137 if (info->format == 1)
138 seq_printf(m, " %u", ext->alt_capability);
139 seq_putc(m, '\n');
Heiko Carstens25502f02012-09-03 14:05:05 +0200140 if (info->nominal_cap)
141 seq_printf(m, "Nominal Capability: %d\n", info->nominal_cap);
142 if (info->secondary_cap)
143 seq_printf(m, "Secondary Capability: %d\n", info->secondary_cap);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200144 for (i = 2; i <= info->cpus_total; i++) {
145 seq_printf(m, "Adjustment %02d-way: %u",
146 i, info->adjustment[i-2]);
147 if (info->format == 1)
148 seq_printf(m, " %u", ext->alt_adjustment[i-2]);
149 seq_putc(m, '\n');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151}
152
Heiko Carstens0facaa12012-09-03 09:38:30 +0200153static void stsi_2_2_2(struct seq_file *m, struct sysinfo_2_2_2 *info)
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200154{
155 if (stsi(info, 2, 2, 2) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200156 return;
Heiko Carstens94f5b092009-03-26 15:23:50 +0100157 EBCASC(info->name, sizeof(info->name));
Heiko Carstens0facaa12012-09-03 09:38:30 +0200158 seq_putc(m, '\n');
159 seq_printf(m, "LPAR Number: %d\n", info->lpar_number);
160 seq_printf(m, "LPAR Characteristics: ");
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200161 if (info->characteristics & LPAR_CHAR_DEDICATED)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200162 seq_printf(m, "Dedicated ");
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200163 if (info->characteristics & LPAR_CHAR_SHARED)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200164 seq_printf(m, "Shared ");
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200165 if (info->characteristics & LPAR_CHAR_LIMITED)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200166 seq_printf(m, "Limited ");
167 seq_putc(m, '\n');
168 seq_printf(m, "LPAR Name: %-8.8s\n", info->name);
169 seq_printf(m, "LPAR Adjustment: %d\n", info->caf);
170 seq_printf(m, "LPAR CPUs Total: %d\n", info->cpus_total);
171 seq_printf(m, "LPAR CPUs Configured: %d\n", info->cpus_configured);
172 seq_printf(m, "LPAR CPUs Standby: %d\n", info->cpus_standby);
173 seq_printf(m, "LPAR CPUs Reserved: %d\n", info->cpus_reserved);
174 seq_printf(m, "LPAR CPUs Dedicated: %d\n", info->cpus_dedicated);
175 seq_printf(m, "LPAR CPUs Shared: %d\n", info->cpus_shared);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200176}
177
Heiko Carstens0facaa12012-09-03 09:38:30 +0200178static void stsi_3_2_2(struct seq_file *m, struct sysinfo_3_2_2 *info)
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200179{
180 int i;
181
182 if (stsi(info, 3, 2, 2) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200183 return;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200184 for (i = 0; i < info->count; i++) {
Heiko Carstens94f5b092009-03-26 15:23:50 +0100185 EBCASC(info->vm[i].name, sizeof(info->vm[i].name));
186 EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi));
Heiko Carstens0facaa12012-09-03 09:38:30 +0200187 seq_putc(m, '\n');
188 seq_printf(m, "VM%02d Name: %-8.8s\n", i, info->vm[i].name);
189 seq_printf(m, "VM%02d Control Program: %-16.16s\n", i, info->vm[i].cpi);
190 seq_printf(m, "VM%02d Adjustment: %d\n", i, info->vm[i].caf);
191 seq_printf(m, "VM%02d CPUs Total: %d\n", i, info->vm[i].cpus_total);
192 seq_printf(m, "VM%02d CPUs Configured: %d\n", i, info->vm[i].cpus_configured);
193 seq_printf(m, "VM%02d CPUs Standby: %d\n", i, info->vm[i].cpus_standby);
194 seq_printf(m, "VM%02d CPUs Reserved: %d\n", i, info->vm[i].cpus_reserved);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196}
197
Heiko Carstens0facaa12012-09-03 09:38:30 +0200198static int sysinfo_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Heiko Carstens0facaa12012-09-03 09:38:30 +0200200 void *info = (void *)get_zeroed_page(GFP_KERNEL);
201 int level;
Heiko Carstens94f5b092009-03-26 15:23:50 +0100202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 if (!info)
204 return 0;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200205 level = stsi_0();
206 if (level >= 1)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200207 stsi_1_1_1(m, info);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200208 if (level >= 1)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200209 stsi_15_1_x(m, info);
Heiko Carstens96f4a702010-10-25 16:10:54 +0200210 if (level >= 1)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200211 stsi_1_2_2(m, info);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200212 if (level >= 2)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200213 stsi_2_2_2(m, info);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200214 if (level >= 3)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200215 stsi_3_2_2(m, info);
216 free_page((unsigned long)info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 return 0;
218}
Heiko Carstens0facaa12012-09-03 09:38:30 +0200219
220static int sysinfo_open(struct inode *inode, struct file *file)
221{
222 return single_open(file, sysinfo_show, NULL);
223}
224
225static const struct file_operations sysinfo_fops = {
226 .open = sysinfo_open,
227 .read = seq_read,
228 .llseek = seq_lseek,
229 .release = single_release,
230};
231
232static int __init sysinfo_create_proc(void)
233{
234 proc_create("sysinfo", 0444, NULL, &sysinfo_fops);
235 return 0;
236}
237device_initcall(sysinfo_create_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
Martin Schwidefsky6bcac502008-12-25 13:38:49 +0100239/*
240 * Service levels interface.
241 */
242
243static DECLARE_RWSEM(service_level_sem);
244static LIST_HEAD(service_level_list);
245
246int register_service_level(struct service_level *slr)
247{
248 struct service_level *ptr;
249
250 down_write(&service_level_sem);
251 list_for_each_entry(ptr, &service_level_list, list)
252 if (ptr == slr) {
253 up_write(&service_level_sem);
254 return -EEXIST;
255 }
256 list_add_tail(&slr->list, &service_level_list);
257 up_write(&service_level_sem);
258 return 0;
259}
260EXPORT_SYMBOL(register_service_level);
261
262int unregister_service_level(struct service_level *slr)
263{
264 struct service_level *ptr, *next;
265 int rc = -ENOENT;
266
267 down_write(&service_level_sem);
268 list_for_each_entry_safe(ptr, next, &service_level_list, list) {
269 if (ptr != slr)
270 continue;
271 list_del(&ptr->list);
272 rc = 0;
273 break;
274 }
275 up_write(&service_level_sem);
276 return rc;
277}
278EXPORT_SYMBOL(unregister_service_level);
279
280static void *service_level_start(struct seq_file *m, loff_t *pos)
281{
282 down_read(&service_level_sem);
283 return seq_list_start(&service_level_list, *pos);
284}
285
286static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
287{
288 return seq_list_next(p, &service_level_list, pos);
289}
290
291static void service_level_stop(struct seq_file *m, void *p)
292{
293 up_read(&service_level_sem);
294}
295
296static int service_level_show(struct seq_file *m, void *p)
297{
298 struct service_level *slr;
299
300 slr = list_entry(p, struct service_level, list);
301 slr->seq_print(m, slr);
302 return 0;
303}
304
305static const struct seq_operations service_level_seq_ops = {
306 .start = service_level_start,
307 .next = service_level_next,
308 .stop = service_level_stop,
309 .show = service_level_show
310};
311
312static int service_level_open(struct inode *inode, struct file *file)
313{
314 return seq_open(file, &service_level_seq_ops);
315}
316
317static const struct file_operations service_level_ops = {
318 .open = service_level_open,
319 .read = seq_read,
320 .llseek = seq_lseek,
321 .release = seq_release
322};
323
324static void service_level_vm_print(struct seq_file *m,
325 struct service_level *slr)
326{
327 char *query_buffer, *str;
328
329 query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA);
330 if (!query_buffer)
331 return;
332 cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL);
333 str = strchr(query_buffer, '\n');
334 if (str)
335 *str = 0;
336 seq_printf(m, "VM: %s\n", query_buffer);
337 kfree(query_buffer);
338}
339
340static struct service_level service_level_vm = {
341 .seq_print = service_level_vm_print
342};
343
344static __init int create_proc_service_level(void)
345{
346 proc_create("service_levels", 0, NULL, &service_level_ops);
347 if (MACHINE_IS_VM)
348 register_service_level(&service_level_vm);
349 return 0;
350}
Martin Schwidefsky6bcac502008-12-25 13:38:49 +0100351subsys_initcall(create_proc_service_level);
352
353/*
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100354 * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
355 */
356void s390_adjust_jiffies(void)
357{
358 struct sysinfo_1_2_2 *info;
359 const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */
360 FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
361 FP_DECL_EX;
362 unsigned int capability;
363
364 info = (void *) get_zeroed_page(GFP_KERNEL);
365 if (!info)
366 return;
367
368 if (stsi(info, 1, 2, 2) != -ENOSYS) {
369 /*
370 * Major sigh. The cpu capability encoding is "special".
371 * If the first 9 bits of info->capability are 0 then it
372 * is a 32 bit unsigned integer in the range 0 .. 2^23.
373 * If the first 9 bits are != 0 then it is a 32 bit float.
374 * In addition a lower value indicates a proportionally
375 * higher cpu capacity. Bogomips are the other way round.
376 * To get to a halfway suitable number we divide 1e7
377 * by the cpu capability number. Yes, that means a floating
378 * point division .. math-emu here we come :-)
379 */
380 FP_UNPACK_SP(SA, &fmil);
381 if ((info->capability >> 23) == 0)
Martin Schwidefsky5b479a72011-10-30 15:17:14 +0100382 FP_FROM_INT_S(SB, (long) info->capability, 64, long);
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100383 else
384 FP_UNPACK_SP(SB, &info->capability);
385 FP_DIV_S(SR, SA, SB);
386 FP_TO_INT_S(capability, SR, 32, 0);
387 } else
388 /*
389 * Really old machine without stsi block for basic
390 * cpu information. Report 42.0 bogomips.
391 */
392 capability = 42;
393 loops_per_jiffy = capability * (500000/HZ);
394 free_page((unsigned long) info);
395}
396
397/*
398 * calibrate the delay loop
399 */
Adrian Bunk6c81c322008-02-06 01:37:51 -0800400void __cpuinit calibrate_delay(void)
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100401{
402 s390_adjust_jiffies();
403 /* Print the good old Bogomips line .. */
404 printk(KERN_DEBUG "Calibrating delay loop (skipped)... "
405 "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
406 (loops_per_jiffy/(5000/HZ)) % 100);
407}