blob: 2249b0cf80ef22c0ff33f6226f7c5241394ecd2b [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
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020025static inline int stsi_0(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070026{
Heiko Carstens94f5b092009-03-26 15:23:50 +010027 int rc = stsi(NULL, 0, 0, 0);
Heiko Carstens0facaa12012-09-03 09:38:30 +020028
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020029 return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -070030}
31
Heiko Carstens0facaa12012-09-03 09:38:30 +020032static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -070033{
Heiko Carstens25502f02012-09-03 14:05:05 +020034 int i;
35
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020036 if (stsi(info, 1, 1, 1) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +020037 return;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020038 EBCASC(info->manufacturer, sizeof(info->manufacturer));
39 EBCASC(info->type, sizeof(info->type));
40 EBCASC(info->model, sizeof(info->model));
41 EBCASC(info->sequence, sizeof(info->sequence));
42 EBCASC(info->plant, sizeof(info->plant));
43 EBCASC(info->model_capacity, sizeof(info->model_capacity));
Martin Schwidefskycbce70e2008-04-17 07:46:10 +020044 EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
45 EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
Heiko Carstens0facaa12012-09-03 09:38:30 +020046 seq_printf(m, "Manufacturer: %-16.16s\n", info->manufacturer);
47 seq_printf(m, "Type: %-4.4s\n", info->type);
48 /*
49 * Sigh: the model field has been renamed with System z9
50 * to model_capacity and a new model field has been added
51 * after the plant field. To avoid confusing older programs
52 * the "Model:" prints "model_capacity model" or just
53 * "model_capacity" if the model string is empty .
54 */
55 seq_printf(m, "Model: %-16.16s", info->model_capacity);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020056 if (info->model[0] != '\0')
Heiko Carstens0facaa12012-09-03 09:38:30 +020057 seq_printf(m, " %-16.16s", info->model);
58 seq_putc(m, '\n');
59 seq_printf(m, "Sequence Code: %-16.16s\n", info->sequence);
60 seq_printf(m, "Plant: %-4.4s\n", info->plant);
61 seq_printf(m, "Model Capacity: %-16.16s %08u\n",
Heiko Carstens25502f02012-09-03 14:05:05 +020062 info->model_capacity, info->model_cap_rating);
63 if (info->model_perm_cap_rating)
Heiko Carstens0facaa12012-09-03 09:38:30 +020064 seq_printf(m, "Model Perm. Capacity: %-16.16s %08u\n",
65 info->model_perm_cap,
Heiko Carstens25502f02012-09-03 14:05:05 +020066 info->model_perm_cap_rating);
67 if (info->model_temp_cap_rating)
Heiko Carstens0facaa12012-09-03 09:38:30 +020068 seq_printf(m, "Model Temp. Capacity: %-16.16s %08u\n",
69 info->model_temp_cap,
Heiko Carstens25502f02012-09-03 14:05:05 +020070 info->model_temp_cap_rating);
71 if (info->ncr)
72 seq_printf(m, "Nominal Cap. Rating: %08u\n", info->ncr);
73 if (info->npr)
74 seq_printf(m, "Nominal Perm. Rating: %08u\n", info->npr);
75 if (info->ntr)
76 seq_printf(m, "Nominal Temp. Rating: %08u\n", info->ntr);
Heiko Carstens7aca2ed2010-10-25 16:10:16 +020077 if (info->cai) {
Heiko Carstens0facaa12012-09-03 09:38:30 +020078 seq_printf(m, "Capacity Adj. Ind.: %d\n", info->cai);
79 seq_printf(m, "Capacity Ch. Reason: %d\n", info->ccr);
Heiko Carstens25502f02012-09-03 14:05:05 +020080 seq_printf(m, "Capacity Transient: %d\n", info->t);
81 }
82 if (info->p) {
83 for (i = 1; i <= ARRAY_SIZE(info->typepct); i++) {
84 seq_printf(m, "Type %d Percentage: %d\n",
85 i, info->typepct[i - 1]);
86 }
Heiko Carstens7aca2ed2010-10-25 16:10:16 +020087 }
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020088}
89
Heiko Carstens0facaa12012-09-03 09:38:30 +020090static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)
Heiko Carstens96f4a702010-10-25 16:10:54 +020091{
92 static int max_mnest;
93 int i, rc;
94
Heiko Carstens0facaa12012-09-03 09:38:30 +020095 seq_putc(m, '\n');
Heiko Carstens96f4a702010-10-25 16:10:54 +020096 if (!MACHINE_HAS_TOPOLOGY)
Heiko Carstens0facaa12012-09-03 09:38:30 +020097 return;
Heiko Carstens96f4a702010-10-25 16:10:54 +020098 if (max_mnest) {
99 stsi(info, 15, 1, max_mnest);
100 } else {
101 for (max_mnest = 6; max_mnest > 1; max_mnest--) {
102 rc = stsi(info, 15, 1, max_mnest);
103 if (rc != -ENOSYS)
104 break;
105 }
106 }
Heiko Carstens0facaa12012-09-03 09:38:30 +0200107 seq_printf(m, "CPU Topology HW: ");
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#ifdef CONFIG_SCHED_MC
Heiko Carstens96f4a702010-10-25 16:10:54 +0200112 store_topology(info);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200113 seq_printf(m, "CPU Topology SW: ");
Heiko Carstens96f4a702010-10-25 16:10:54 +0200114 for (i = 0; i < TOPOLOGY_NR_MAG; i++)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200115 seq_printf(m, " %d", info->mag[i]);
116 seq_putc(m, '\n');
Heiko Carstens8d11e022010-10-29 16:50:37 +0200117#endif
Heiko Carstens96f4a702010-10-25 16:10:54 +0200118}
119
Heiko Carstens0facaa12012-09-03 09:38:30 +0200120static void stsi_1_2_2(struct seq_file *m, struct sysinfo_1_2_2 *info)
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200121{
122 struct sysinfo_1_2_2_extension *ext;
123 int i;
124
125 if (stsi(info, 1, 2, 2) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200126 return;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200127 ext = (struct sysinfo_1_2_2_extension *)
128 ((unsigned long) info + info->acc_offset);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200129 seq_printf(m, "CPUs Total: %d\n", info->cpus_total);
130 seq_printf(m, "CPUs Configured: %d\n", info->cpus_configured);
131 seq_printf(m, "CPUs Standby: %d\n", info->cpus_standby);
132 seq_printf(m, "CPUs Reserved: %d\n", info->cpus_reserved);
133 /*
134 * Sigh 2. According to the specification the alternate
135 * capability field is a 32 bit floating point number
136 * if the higher order 8 bits are not zero. Printing
137 * a floating point number in the kernel is a no-no,
138 * always print the number as 32 bit unsigned integer.
139 * The user-space needs to know about the strange
140 * encoding of the alternate cpu capability.
141 */
142 seq_printf(m, "Capability: %u", info->capability);
143 if (info->format == 1)
144 seq_printf(m, " %u", ext->alt_capability);
145 seq_putc(m, '\n');
Heiko Carstens25502f02012-09-03 14:05:05 +0200146 if (info->nominal_cap)
147 seq_printf(m, "Nominal Capability: %d\n", info->nominal_cap);
148 if (info->secondary_cap)
149 seq_printf(m, "Secondary Capability: %d\n", info->secondary_cap);
Heiko Carstens0facaa12012-09-03 09:38:30 +0200150 for (i = 2; i <= info->cpus_total; i++) {
151 seq_printf(m, "Adjustment %02d-way: %u",
152 i, info->adjustment[i-2]);
153 if (info->format == 1)
154 seq_printf(m, " %u", ext->alt_adjustment[i-2]);
155 seq_putc(m, '\n');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}
158
Heiko Carstens0facaa12012-09-03 09:38:30 +0200159static void stsi_2_2_2(struct seq_file *m, struct sysinfo_2_2_2 *info)
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200160{
161 if (stsi(info, 2, 2, 2) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200162 return;
Heiko Carstens94f5b092009-03-26 15:23:50 +0100163 EBCASC(info->name, sizeof(info->name));
Heiko Carstens0facaa12012-09-03 09:38:30 +0200164 seq_putc(m, '\n');
165 seq_printf(m, "LPAR Number: %d\n", info->lpar_number);
166 seq_printf(m, "LPAR Characteristics: ");
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200167 if (info->characteristics & LPAR_CHAR_DEDICATED)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200168 seq_printf(m, "Dedicated ");
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200169 if (info->characteristics & LPAR_CHAR_SHARED)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200170 seq_printf(m, "Shared ");
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200171 if (info->characteristics & LPAR_CHAR_LIMITED)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200172 seq_printf(m, "Limited ");
173 seq_putc(m, '\n');
174 seq_printf(m, "LPAR Name: %-8.8s\n", info->name);
175 seq_printf(m, "LPAR Adjustment: %d\n", info->caf);
176 seq_printf(m, "LPAR CPUs Total: %d\n", info->cpus_total);
177 seq_printf(m, "LPAR CPUs Configured: %d\n", info->cpus_configured);
178 seq_printf(m, "LPAR CPUs Standby: %d\n", info->cpus_standby);
179 seq_printf(m, "LPAR CPUs Reserved: %d\n", info->cpus_reserved);
180 seq_printf(m, "LPAR CPUs Dedicated: %d\n", info->cpus_dedicated);
181 seq_printf(m, "LPAR CPUs Shared: %d\n", info->cpus_shared);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200182}
183
Heiko Carstens0facaa12012-09-03 09:38:30 +0200184static void stsi_3_2_2(struct seq_file *m, struct sysinfo_3_2_2 *info)
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200185{
186 int i;
187
188 if (stsi(info, 3, 2, 2) == -ENOSYS)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200189 return;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200190 for (i = 0; i < info->count; i++) {
Heiko Carstens94f5b092009-03-26 15:23:50 +0100191 EBCASC(info->vm[i].name, sizeof(info->vm[i].name));
192 EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi));
Heiko Carstens0facaa12012-09-03 09:38:30 +0200193 seq_putc(m, '\n');
194 seq_printf(m, "VM%02d Name: %-8.8s\n", i, info->vm[i].name);
195 seq_printf(m, "VM%02d Control Program: %-16.16s\n", i, info->vm[i].cpi);
196 seq_printf(m, "VM%02d Adjustment: %d\n", i, info->vm[i].caf);
197 seq_printf(m, "VM%02d CPUs Total: %d\n", i, info->vm[i].cpus_total);
198 seq_printf(m, "VM%02d CPUs Configured: %d\n", i, info->vm[i].cpus_configured);
199 seq_printf(m, "VM%02d CPUs Standby: %d\n", i, info->vm[i].cpus_standby);
200 seq_printf(m, "VM%02d CPUs Reserved: %d\n", i, info->vm[i].cpus_reserved);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
Heiko Carstens0facaa12012-09-03 09:38:30 +0200204static int sysinfo_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
Heiko Carstens0facaa12012-09-03 09:38:30 +0200206 void *info = (void *)get_zeroed_page(GFP_KERNEL);
207 int level;
Heiko Carstens94f5b092009-03-26 15:23:50 +0100208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 if (!info)
210 return 0;
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200211 level = stsi_0();
212 if (level >= 1)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200213 stsi_1_1_1(m, info);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200214 if (level >= 1)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200215 stsi_15_1_x(m, info);
Heiko Carstens96f4a702010-10-25 16:10:54 +0200216 if (level >= 1)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200217 stsi_1_2_2(m, info);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200218 if (level >= 2)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200219 stsi_2_2_2(m, info);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200220 if (level >= 3)
Heiko Carstens0facaa12012-09-03 09:38:30 +0200221 stsi_3_2_2(m, info);
222 free_page((unsigned long)info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 return 0;
224}
Heiko Carstens0facaa12012-09-03 09:38:30 +0200225
226static int sysinfo_open(struct inode *inode, struct file *file)
227{
228 return single_open(file, sysinfo_show, NULL);
229}
230
231static const struct file_operations sysinfo_fops = {
232 .open = sysinfo_open,
233 .read = seq_read,
234 .llseek = seq_lseek,
235 .release = single_release,
236};
237
238static int __init sysinfo_create_proc(void)
239{
240 proc_create("sysinfo", 0444, NULL, &sysinfo_fops);
241 return 0;
242}
243device_initcall(sysinfo_create_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Martin Schwidefsky6bcac502008-12-25 13:38:49 +0100245/*
246 * Service levels interface.
247 */
248
249static DECLARE_RWSEM(service_level_sem);
250static LIST_HEAD(service_level_list);
251
252int register_service_level(struct service_level *slr)
253{
254 struct service_level *ptr;
255
256 down_write(&service_level_sem);
257 list_for_each_entry(ptr, &service_level_list, list)
258 if (ptr == slr) {
259 up_write(&service_level_sem);
260 return -EEXIST;
261 }
262 list_add_tail(&slr->list, &service_level_list);
263 up_write(&service_level_sem);
264 return 0;
265}
266EXPORT_SYMBOL(register_service_level);
267
268int unregister_service_level(struct service_level *slr)
269{
270 struct service_level *ptr, *next;
271 int rc = -ENOENT;
272
273 down_write(&service_level_sem);
274 list_for_each_entry_safe(ptr, next, &service_level_list, list) {
275 if (ptr != slr)
276 continue;
277 list_del(&ptr->list);
278 rc = 0;
279 break;
280 }
281 up_write(&service_level_sem);
282 return rc;
283}
284EXPORT_SYMBOL(unregister_service_level);
285
286static void *service_level_start(struct seq_file *m, loff_t *pos)
287{
288 down_read(&service_level_sem);
289 return seq_list_start(&service_level_list, *pos);
290}
291
292static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
293{
294 return seq_list_next(p, &service_level_list, pos);
295}
296
297static void service_level_stop(struct seq_file *m, void *p)
298{
299 up_read(&service_level_sem);
300}
301
302static int service_level_show(struct seq_file *m, void *p)
303{
304 struct service_level *slr;
305
306 slr = list_entry(p, struct service_level, list);
307 slr->seq_print(m, slr);
308 return 0;
309}
310
311static const struct seq_operations service_level_seq_ops = {
312 .start = service_level_start,
313 .next = service_level_next,
314 .stop = service_level_stop,
315 .show = service_level_show
316};
317
318static int service_level_open(struct inode *inode, struct file *file)
319{
320 return seq_open(file, &service_level_seq_ops);
321}
322
323static const struct file_operations service_level_ops = {
324 .open = service_level_open,
325 .read = seq_read,
326 .llseek = seq_lseek,
327 .release = seq_release
328};
329
330static void service_level_vm_print(struct seq_file *m,
331 struct service_level *slr)
332{
333 char *query_buffer, *str;
334
335 query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA);
336 if (!query_buffer)
337 return;
338 cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL);
339 str = strchr(query_buffer, '\n');
340 if (str)
341 *str = 0;
342 seq_printf(m, "VM: %s\n", query_buffer);
343 kfree(query_buffer);
344}
345
346static struct service_level service_level_vm = {
347 .seq_print = service_level_vm_print
348};
349
350static __init int create_proc_service_level(void)
351{
352 proc_create("service_levels", 0, NULL, &service_level_ops);
353 if (MACHINE_IS_VM)
354 register_service_level(&service_level_vm);
355 return 0;
356}
Martin Schwidefsky6bcac502008-12-25 13:38:49 +0100357subsys_initcall(create_proc_service_level);
358
359/*
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100360 * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
361 */
362void s390_adjust_jiffies(void)
363{
364 struct sysinfo_1_2_2 *info;
365 const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */
366 FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
367 FP_DECL_EX;
368 unsigned int capability;
369
370 info = (void *) get_zeroed_page(GFP_KERNEL);
371 if (!info)
372 return;
373
374 if (stsi(info, 1, 2, 2) != -ENOSYS) {
375 /*
376 * Major sigh. The cpu capability encoding is "special".
377 * If the first 9 bits of info->capability are 0 then it
378 * is a 32 bit unsigned integer in the range 0 .. 2^23.
379 * If the first 9 bits are != 0 then it is a 32 bit float.
380 * In addition a lower value indicates a proportionally
381 * higher cpu capacity. Bogomips are the other way round.
382 * To get to a halfway suitable number we divide 1e7
383 * by the cpu capability number. Yes, that means a floating
384 * point division .. math-emu here we come :-)
385 */
386 FP_UNPACK_SP(SA, &fmil);
387 if ((info->capability >> 23) == 0)
Martin Schwidefsky5b479a72011-10-30 15:17:14 +0100388 FP_FROM_INT_S(SB, (long) info->capability, 64, long);
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100389 else
390 FP_UNPACK_SP(SB, &info->capability);
391 FP_DIV_S(SR, SA, SB);
392 FP_TO_INT_S(capability, SR, 32, 0);
393 } else
394 /*
395 * Really old machine without stsi block for basic
396 * cpu information. Report 42.0 bogomips.
397 */
398 capability = 42;
399 loops_per_jiffy = capability * (500000/HZ);
400 free_page((unsigned long) info);
401}
402
403/*
404 * calibrate the delay loop
405 */
Adrian Bunk6c81c322008-02-06 01:37:51 -0800406void __cpuinit calibrate_delay(void)
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100407{
408 s390_adjust_jiffies();
409 /* Print the good old Bogomips line .. */
410 printk(KERN_DEBUG "Calibrating delay loop (skipped)... "
411 "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
412 (loops_per_jiffy/(5000/HZ)) % 100);
413}