diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 07:38:49 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 07:39:00 -0500 |
commit | 6bcac508fbebdca52f5a55d69a4316997ecb5391 (patch) | |
tree | fa04ce94ef6267676e8edcd2f93ef776bd6e60eb /drivers/s390 | |
parent | 7a0b4cbc7d7d99763511b488b08bbc2607ddd1e3 (diff) |
[S390] service level interface.
Add a new proc interface /proc/service_levels that allows any code
to report a relevant service level, e.g. the microcode level of
devices, the service level of the hypervisor, etc.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/qeth_core.h | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 11 | ||||
-rw-r--r-- | drivers/s390/sysinfo.c | 127 |
3 files changed, 138 insertions, 2 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index af6d60458513..1414d3df69dc 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <asm/qdio.h> | 31 | #include <asm/qdio.h> |
32 | #include <asm/ccwdev.h> | 32 | #include <asm/ccwdev.h> |
33 | #include <asm/ccwgroup.h> | 33 | #include <asm/ccwgroup.h> |
34 | #include <asm/sysinfo.h> | ||
34 | 35 | ||
35 | #include "qeth_core_mpc.h" | 36 | #include "qeth_core_mpc.h" |
36 | 37 | ||
@@ -733,6 +734,7 @@ struct qeth_card { | |||
733 | struct qeth_osn_info osn_info; | 734 | struct qeth_osn_info osn_info; |
734 | struct qeth_discipline discipline; | 735 | struct qeth_discipline discipline; |
735 | atomic_t force_alloc_skb; | 736 | atomic_t force_alloc_skb; |
737 | struct service_level qeth_service_level; | ||
736 | }; | 738 | }; |
737 | 739 | ||
738 | struct qeth_card_list_struct { | 740 | struct qeth_card_list_struct { |
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index ebdc549da537..3dc752c29c73 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c | |||
@@ -1138,6 +1138,14 @@ static int qeth_setup_card(struct qeth_card *card) | |||
1138 | return 0; | 1138 | return 0; |
1139 | } | 1139 | } |
1140 | 1140 | ||
1141 | static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr) | ||
1142 | { | ||
1143 | struct qeth_card *card = container_of(slr, struct qeth_card, | ||
1144 | qeth_service_level); | ||
1145 | seq_printf(m, "qeth: %s firmware level %s\n", CARD_BUS_ID(card), | ||
1146 | card->info.mcl_level); | ||
1147 | } | ||
1148 | |||
1141 | static struct qeth_card *qeth_alloc_card(void) | 1149 | static struct qeth_card *qeth_alloc_card(void) |
1142 | { | 1150 | { |
1143 | struct qeth_card *card; | 1151 | struct qeth_card *card; |
@@ -1157,6 +1165,8 @@ static struct qeth_card *qeth_alloc_card(void) | |||
1157 | return NULL; | 1165 | return NULL; |
1158 | } | 1166 | } |
1159 | card->options.layer2 = -1; | 1167 | card->options.layer2 = -1; |
1168 | card->qeth_service_level.seq_print = qeth_core_sl_print; | ||
1169 | register_service_level(&card->qeth_service_level); | ||
1160 | return card; | 1170 | return card; |
1161 | } | 1171 | } |
1162 | 1172 | ||
@@ -3730,6 +3740,7 @@ static void qeth_core_free_card(struct qeth_card *card) | |||
3730 | free_netdev(card->dev); | 3740 | free_netdev(card->dev); |
3731 | kfree(card->ip_tbd_list); | 3741 | kfree(card->ip_tbd_list); |
3732 | qeth_free_qdio_buffers(card); | 3742 | qeth_free_qdio_buffers(card); |
3743 | unregister_service_level(&card->qeth_service_level); | ||
3733 | kfree(card); | 3744 | kfree(card); |
3734 | } | 3745 | } |
3735 | 3746 | ||
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c index c3e4ab07b9cc..0eea90781385 100644 --- a/drivers/s390/sysinfo.c +++ b/drivers/s390/sysinfo.c | |||
@@ -1,17 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/sysinfo.c | 2 | * drivers/s390/sysinfo.c |
3 | * | 3 | * |
4 | * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation | 4 | * Copyright IBM Corp. 2001, 2008 |
5 | * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com) | 5 | * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com) |
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
6 | */ | 7 | */ |
7 | 8 | ||
8 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
9 | #include <linux/mm.h> | 10 | #include <linux/mm.h> |
10 | #include <linux/proc_fs.h> | 11 | #include <linux/proc_fs.h> |
12 | #include <linux/seq_file.h> | ||
11 | #include <linux/init.h> | 13 | #include <linux/init.h> |
12 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/module.h> | ||
13 | #include <asm/ebcdic.h> | 16 | #include <asm/ebcdic.h> |
14 | #include <asm/sysinfo.h> | 17 | #include <asm/sysinfo.h> |
18 | #include <asm/cpcmd.h> | ||
15 | 19 | ||
16 | /* Sigh, math-emu. Don't ask. */ | 20 | /* Sigh, math-emu. Don't ask. */ |
17 | #include <asm/sfp-util.h> | 21 | #include <asm/sfp-util.h> |
@@ -271,6 +275,125 @@ static __init int create_proc_sysinfo(void) | |||
271 | 275 | ||
272 | __initcall(create_proc_sysinfo); | 276 | __initcall(create_proc_sysinfo); |
273 | 277 | ||
278 | /* | ||
279 | * Service levels interface. | ||
280 | */ | ||
281 | |||
282 | static DECLARE_RWSEM(service_level_sem); | ||
283 | static LIST_HEAD(service_level_list); | ||
284 | |||
285 | int register_service_level(struct service_level *slr) | ||
286 | { | ||
287 | struct service_level *ptr; | ||
288 | |||
289 | down_write(&service_level_sem); | ||
290 | list_for_each_entry(ptr, &service_level_list, list) | ||
291 | if (ptr == slr) { | ||
292 | up_write(&service_level_sem); | ||
293 | return -EEXIST; | ||
294 | } | ||
295 | list_add_tail(&slr->list, &service_level_list); | ||
296 | up_write(&service_level_sem); | ||
297 | return 0; | ||
298 | } | ||
299 | EXPORT_SYMBOL(register_service_level); | ||
300 | |||
301 | int unregister_service_level(struct service_level *slr) | ||
302 | { | ||
303 | struct service_level *ptr, *next; | ||
304 | int rc = -ENOENT; | ||
305 | |||
306 | down_write(&service_level_sem); | ||
307 | list_for_each_entry_safe(ptr, next, &service_level_list, list) { | ||
308 | if (ptr != slr) | ||
309 | continue; | ||
310 | list_del(&ptr->list); | ||
311 | rc = 0; | ||
312 | break; | ||
313 | } | ||
314 | up_write(&service_level_sem); | ||
315 | return rc; | ||
316 | } | ||
317 | EXPORT_SYMBOL(unregister_service_level); | ||
318 | |||
319 | static void *service_level_start(struct seq_file *m, loff_t *pos) | ||
320 | { | ||
321 | down_read(&service_level_sem); | ||
322 | return seq_list_start(&service_level_list, *pos); | ||
323 | } | ||
324 | |||
325 | static void *service_level_next(struct seq_file *m, void *p, loff_t *pos) | ||
326 | { | ||
327 | return seq_list_next(p, &service_level_list, pos); | ||
328 | } | ||
329 | |||
330 | static void service_level_stop(struct seq_file *m, void *p) | ||
331 | { | ||
332 | up_read(&service_level_sem); | ||
333 | } | ||
334 | |||
335 | static int service_level_show(struct seq_file *m, void *p) | ||
336 | { | ||
337 | struct service_level *slr; | ||
338 | |||
339 | slr = list_entry(p, struct service_level, list); | ||
340 | slr->seq_print(m, slr); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static const struct seq_operations service_level_seq_ops = { | ||
345 | .start = service_level_start, | ||
346 | .next = service_level_next, | ||
347 | .stop = service_level_stop, | ||
348 | .show = service_level_show | ||
349 | }; | ||
350 | |||
351 | static int service_level_open(struct inode *inode, struct file *file) | ||
352 | { | ||
353 | return seq_open(file, &service_level_seq_ops); | ||
354 | } | ||
355 | |||
356 | static const struct file_operations service_level_ops = { | ||
357 | .open = service_level_open, | ||
358 | .read = seq_read, | ||
359 | .llseek = seq_lseek, | ||
360 | .release = seq_release | ||
361 | }; | ||
362 | |||
363 | static void service_level_vm_print(struct seq_file *m, | ||
364 | struct service_level *slr) | ||
365 | { | ||
366 | char *query_buffer, *str; | ||
367 | |||
368 | query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA); | ||
369 | if (!query_buffer) | ||
370 | return; | ||
371 | cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL); | ||
372 | str = strchr(query_buffer, '\n'); | ||
373 | if (str) | ||
374 | *str = 0; | ||
375 | seq_printf(m, "VM: %s\n", query_buffer); | ||
376 | kfree(query_buffer); | ||
377 | } | ||
378 | |||
379 | static struct service_level service_level_vm = { | ||
380 | .seq_print = service_level_vm_print | ||
381 | }; | ||
382 | |||
383 | static __init int create_proc_service_level(void) | ||
384 | { | ||
385 | proc_create("service_levels", 0, NULL, &service_level_ops); | ||
386 | if (MACHINE_IS_VM) | ||
387 | register_service_level(&service_level_vm); | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | subsys_initcall(create_proc_service_level); | ||
392 | |||
393 | /* | ||
394 | * Bogomips calculation based on cpu capability. | ||
395 | */ | ||
396 | |||
274 | int get_cpu_capability(unsigned int *capability) | 397 | int get_cpu_capability(unsigned int *capability) |
275 | { | 398 | { |
276 | struct sysinfo_1_2_2 *info; | 399 | struct sysinfo_1_2_2 *info; |