Root/arch/s390/kernel/sysinfo.c

1/*
2 * Copyright IBM Corp. 2001, 2009
3 * Author(s): Ulrich Weigand <Ulrich.Weigand@de.ibm.com>,
4 * Martin Schwidefsky <schwidefsky@de.ibm.com>,
5 */
6
7#include <linux/kernel.h>
8#include <linux/mm.h>
9#include <linux/proc_fs.h>
10#include <linux/seq_file.h>
11#include <linux/init.h>
12#include <linux/delay.h>
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <asm/ebcdic.h>
16#include <asm/sysinfo.h>
17#include <asm/cpcmd.h>
18#include <asm/topology.h>
19
20/* 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
25static inline int stsi_0(void)
26{
27    int rc = stsi(NULL, 0, 0, 0);
28    return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
29}
30
31static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
32{
33    if (stsi(info, 1, 1, 1) == -ENOSYS)
34        return len;
35
36    EBCASC(info->manufacturer, sizeof(info->manufacturer));
37    EBCASC(info->type, sizeof(info->type));
38    EBCASC(info->model, sizeof(info->model));
39    EBCASC(info->sequence, sizeof(info->sequence));
40    EBCASC(info->plant, sizeof(info->plant));
41    EBCASC(info->model_capacity, sizeof(info->model_capacity));
42    EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
43    EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
44    len += sprintf(page + len, "Manufacturer: %-16.16s\n",
45               info->manufacturer);
46    len += sprintf(page + len, "Type: %-4.4s\n",
47               info->type);
48    if (info->model[0] != '\0')
49        /*
50         * Sigh: the model field has been renamed with System z9
51         * to model_capacity and a new model field has been added
52         * after the plant field. To avoid confusing older programs
53         * the "Model:" prints "model_capacity model" or just
54         * "model_capacity" if the model string is empty .
55         */
56        len += sprintf(page + len,
57                   "Model: %-16.16s %-16.16s\n",
58                   info->model_capacity, info->model);
59    else
60        len += sprintf(page + len, "Model: %-16.16s\n",
61                   info->model_capacity);
62    len += sprintf(page + len, "Sequence Code: %-16.16s\n",
63               info->sequence);
64    len += sprintf(page + len, "Plant: %-4.4s\n",
65               info->plant);
66    len += sprintf(page + len, "Model Capacity: %-16.16s %08u\n",
67               info->model_capacity, *(u32 *) info->model_cap_rating);
68    if (info->model_perm_cap[0] != '\0')
69        len += sprintf(page + len,
70                   "Model Perm. Capacity: %-16.16s %08u\n",
71                   info->model_perm_cap,
72                   *(u32 *) info->model_perm_cap_rating);
73    if (info->model_temp_cap[0] != '\0')
74        len += sprintf(page + len,
75                   "Model Temp. Capacity: %-16.16s %08u\n",
76                   info->model_temp_cap,
77                   *(u32 *) info->model_temp_cap_rating);
78    if (info->cai) {
79        len += sprintf(page + len,
80                   "Capacity Adj. Ind.: %d\n",
81                   info->cai);
82        len += sprintf(page + len, "Capacity Ch. Reason: %d\n",
83                   info->ccr);
84    }
85    return len;
86}
87
88static int stsi_15_1_x(struct sysinfo_15_1_x *info, char *page, int len)
89{
90    static int max_mnest;
91    int i, rc;
92
93    len += sprintf(page + len, "\n");
94    if (!MACHINE_HAS_TOPOLOGY)
95        return len;
96    if (max_mnest) {
97        stsi(info, 15, 1, max_mnest);
98    } else {
99        for (max_mnest = 6; max_mnest > 1; max_mnest--) {
100            rc = stsi(info, 15, 1, max_mnest);
101            if (rc != -ENOSYS)
102                break;
103        }
104    }
105    len += sprintf(page + len, "CPU Topology HW: ");
106    for (i = 0; i < TOPOLOGY_NR_MAG; i++)
107        len += sprintf(page + len, " %d", info->mag[i]);
108    len += sprintf(page + len, "\n");
109#ifdef CONFIG_SCHED_MC
110    store_topology(info);
111    len += sprintf(page + len, "CPU Topology SW: ");
112    for (i = 0; i < TOPOLOGY_NR_MAG; i++)
113        len += sprintf(page + len, " %d", info->mag[i]);
114    len += sprintf(page + len, "\n");
115#endif
116    return len;
117}
118
119static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
120{
121    struct sysinfo_1_2_2_extension *ext;
122    int i;
123
124    if (stsi(info, 1, 2, 2) == -ENOSYS)
125        return len;
126    ext = (struct sysinfo_1_2_2_extension *)
127        ((unsigned long) info + info->acc_offset);
128
129    len += sprintf(page + len, "CPUs Total: %d\n",
130               info->cpus_total);
131    len += sprintf(page + len, "CPUs Configured: %d\n",
132               info->cpus_configured);
133    len += sprintf(page + len, "CPUs Standby: %d\n",
134               info->cpus_standby);
135    len += sprintf(page + len, "CPUs Reserved: %d\n",
136               info->cpus_reserved);
137
138    if (info->format == 1) {
139        /*
140         * Sigh 2. According to the specification the alternate
141         * capability field is a 32 bit floating point number
142         * if the higher order 8 bits are not zero. Printing
143         * a floating point number in the kernel is a no-no,
144         * always print the number as 32 bit unsigned integer.
145         * The user-space needs to know about the strange
146         * encoding of the alternate cpu capability.
147         */
148        len += sprintf(page + len, "Capability: %u %u\n",
149                   info->capability, ext->alt_capability);
150        for (i = 2; i <= info->cpus_total; i++)
151            len += sprintf(page + len,
152                       "Adjustment %02d-way: %u %u\n",
153                       i, info->adjustment[i-2],
154                       ext->alt_adjustment[i-2]);
155
156    } else {
157        len += sprintf(page + len, "Capability: %u\n",
158                   info->capability);
159        for (i = 2; i <= info->cpus_total; i++)
160            len += sprintf(page + len,
161                       "Adjustment %02d-way: %u\n",
162                       i, info->adjustment[i-2]);
163    }
164
165    if (info->secondary_capability != 0)
166        len += sprintf(page + len, "Secondary Capability: %d\n",
167                   info->secondary_capability);
168    return len;
169}
170
171static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len)
172{
173    if (stsi(info, 2, 2, 2) == -ENOSYS)
174        return len;
175
176    EBCASC(info->name, sizeof(info->name));
177
178    len += sprintf(page + len, "\n");
179    len += sprintf(page + len, "LPAR Number: %d\n",
180               info->lpar_number);
181
182    len += sprintf(page + len, "LPAR Characteristics: ");
183    if (info->characteristics & LPAR_CHAR_DEDICATED)
184        len += sprintf(page + len, "Dedicated ");
185    if (info->characteristics & LPAR_CHAR_SHARED)
186        len += sprintf(page + len, "Shared ");
187    if (info->characteristics & LPAR_CHAR_LIMITED)
188        len += sprintf(page + len, "Limited ");
189    len += sprintf(page + len, "\n");
190
191    len += sprintf(page + len, "LPAR Name: %-8.8s\n",
192               info->name);
193
194    len += sprintf(page + len, "LPAR Adjustment: %d\n",
195               info->caf);
196
197    len += sprintf(page + len, "LPAR CPUs Total: %d\n",
198               info->cpus_total);
199    len += sprintf(page + len, "LPAR CPUs Configured: %d\n",
200               info->cpus_configured);
201    len += sprintf(page + len, "LPAR CPUs Standby: %d\n",
202               info->cpus_standby);
203    len += sprintf(page + len, "LPAR CPUs Reserved: %d\n",
204               info->cpus_reserved);
205    len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n",
206               info->cpus_dedicated);
207    len += sprintf(page + len, "LPAR CPUs Shared: %d\n",
208               info->cpus_shared);
209    return len;
210}
211
212static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len)
213{
214    int i;
215
216    if (stsi(info, 3, 2, 2) == -ENOSYS)
217        return len;
218    for (i = 0; i < info->count; i++) {
219        EBCASC(info->vm[i].name, sizeof(info->vm[i].name));
220        EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi));
221        len += sprintf(page + len, "\n");
222        len += sprintf(page + len, "VM%02d Name: %-8.8s\n",
223                   i, info->vm[i].name);
224        len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n",
225                   i, info->vm[i].cpi);
226
227        len += sprintf(page + len, "VM%02d Adjustment: %d\n",
228                   i, info->vm[i].caf);
229
230        len += sprintf(page + len, "VM%02d CPUs Total: %d\n",
231                   i, info->vm[i].cpus_total);
232        len += sprintf(page + len, "VM%02d CPUs Configured: %d\n",
233                   i, info->vm[i].cpus_configured);
234        len += sprintf(page + len, "VM%02d CPUs Standby: %d\n",
235                   i, info->vm[i].cpus_standby);
236        len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n",
237                   i, info->vm[i].cpus_reserved);
238    }
239    return len;
240}
241
242static int proc_read_sysinfo(char *page, char **start,
243                 off_t off, int count,
244                 int *eof, void *data)
245{
246    unsigned long info = get_zeroed_page(GFP_KERNEL);
247    int level, len;
248
249    if (!info)
250        return 0;
251
252    len = 0;
253    level = stsi_0();
254    if (level >= 1)
255        len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len);
256
257    if (level >= 1)
258        len = stsi_15_1_x((struct sysinfo_15_1_x *) info, page, len);
259
260    if (level >= 1)
261        len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len);
262
263    if (level >= 2)
264        len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len);
265
266    if (level >= 3)
267        len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len);
268
269    free_page(info);
270    return len;
271}
272
273static __init int create_proc_sysinfo(void)
274{
275    create_proc_read_entry("sysinfo", 0444, NULL,
276                   proc_read_sysinfo, NULL);
277    return 0;
278}
279device_initcall(create_proc_sysinfo);
280
281/*
282 * Service levels interface.
283 */
284
285static DECLARE_RWSEM(service_level_sem);
286static LIST_HEAD(service_level_list);
287
288int register_service_level(struct service_level *slr)
289{
290    struct service_level *ptr;
291
292    down_write(&service_level_sem);
293    list_for_each_entry(ptr, &service_level_list, list)
294        if (ptr == slr) {
295            up_write(&service_level_sem);
296            return -EEXIST;
297        }
298    list_add_tail(&slr->list, &service_level_list);
299    up_write(&service_level_sem);
300    return 0;
301}
302EXPORT_SYMBOL(register_service_level);
303
304int unregister_service_level(struct service_level *slr)
305{
306    struct service_level *ptr, *next;
307    int rc = -ENOENT;
308
309    down_write(&service_level_sem);
310    list_for_each_entry_safe(ptr, next, &service_level_list, list) {
311        if (ptr != slr)
312            continue;
313        list_del(&ptr->list);
314        rc = 0;
315        break;
316    }
317    up_write(&service_level_sem);
318    return rc;
319}
320EXPORT_SYMBOL(unregister_service_level);
321
322static void *service_level_start(struct seq_file *m, loff_t *pos)
323{
324    down_read(&service_level_sem);
325    return seq_list_start(&service_level_list, *pos);
326}
327
328static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
329{
330    return seq_list_next(p, &service_level_list, pos);
331}
332
333static void service_level_stop(struct seq_file *m, void *p)
334{
335    up_read(&service_level_sem);
336}
337
338static int service_level_show(struct seq_file *m, void *p)
339{
340    struct service_level *slr;
341
342    slr = list_entry(p, struct service_level, list);
343    slr->seq_print(m, slr);
344    return 0;
345}
346
347static const struct seq_operations service_level_seq_ops = {
348    .start = service_level_start,
349    .next = service_level_next,
350    .stop = service_level_stop,
351    .show = service_level_show
352};
353
354static int service_level_open(struct inode *inode, struct file *file)
355{
356    return seq_open(file, &service_level_seq_ops);
357}
358
359static const struct file_operations service_level_ops = {
360    .open = service_level_open,
361    .read = seq_read,
362    .llseek = seq_lseek,
363    .release = seq_release
364};
365
366static void service_level_vm_print(struct seq_file *m,
367                   struct service_level *slr)
368{
369    char *query_buffer, *str;
370
371    query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA);
372    if (!query_buffer)
373        return;
374    cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL);
375    str = strchr(query_buffer, '\n');
376    if (str)
377        *str = 0;
378    seq_printf(m, "VM: %s\n", query_buffer);
379    kfree(query_buffer);
380}
381
382static struct service_level service_level_vm = {
383    .seq_print = service_level_vm_print
384};
385
386static __init int create_proc_service_level(void)
387{
388    proc_create("service_levels", 0, NULL, &service_level_ops);
389    if (MACHINE_IS_VM)
390        register_service_level(&service_level_vm);
391    return 0;
392}
393subsys_initcall(create_proc_service_level);
394
395/*
396 * Bogomips calculation based on cpu capability.
397 */
398int get_cpu_capability(unsigned int *capability)
399{
400    struct sysinfo_1_2_2 *info;
401    int rc;
402
403    info = (void *) get_zeroed_page(GFP_KERNEL);
404    if (!info)
405        return -ENOMEM;
406    rc = stsi(info, 1, 2, 2);
407    if (rc == -ENOSYS)
408        goto out;
409    rc = 0;
410    *capability = info->capability;
411out:
412    free_page((unsigned long) info);
413    return rc;
414}
415
416/*
417 * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
418 */
419void s390_adjust_jiffies(void)
420{
421    struct sysinfo_1_2_2 *info;
422    const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */
423    FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
424    FP_DECL_EX;
425    unsigned int capability;
426
427    info = (void *) get_zeroed_page(GFP_KERNEL);
428    if (!info)
429        return;
430
431    if (stsi(info, 1, 2, 2) != -ENOSYS) {
432        /*
433         * Major sigh. The cpu capability encoding is "special".
434         * If the first 9 bits of info->capability are 0 then it
435         * is a 32 bit unsigned integer in the range 0 .. 2^23.
436         * If the first 9 bits are != 0 then it is a 32 bit float.
437         * In addition a lower value indicates a proportionally
438         * higher cpu capacity. Bogomips are the other way round.
439         * To get to a halfway suitable number we divide 1e7
440         * by the cpu capability number. Yes, that means a floating
441         * point division .. math-emu here we come :-)
442         */
443        FP_UNPACK_SP(SA, &fmil);
444        if ((info->capability >> 23) == 0)
445            FP_FROM_INT_S(SB, info->capability, 32, int);
446        else
447            FP_UNPACK_SP(SB, &info->capability);
448        FP_DIV_S(SR, SA, SB);
449        FP_TO_INT_S(capability, SR, 32, 0);
450    } else
451        /*
452         * Really old machine without stsi block for basic
453         * cpu information. Report 42.0 bogomips.
454         */
455        capability = 42;
456    loops_per_jiffy = capability * (500000/HZ);
457    free_page((unsigned long) info);
458}
459
460/*
461 * calibrate the delay loop
462 */
463void __cpuinit calibrate_delay(void)
464{
465    s390_adjust_jiffies();
466    /* Print the good old Bogomips line .. */
467    printk(KERN_DEBUG "Calibrating delay loop (skipped)... "
468           "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
469           (loops_per_jiffy/(5000/HZ)) % 100);
470}
471

Archive Download this file



interactive