diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/con3215.c | 205 | ||||
-rw-r--r-- | drivers/s390/char/con3270.c | 12 | ||||
-rw-r--r-- | drivers/s390/char/fs3270.c | 16 | ||||
-rw-r--r-- | drivers/s390/char/monreader.c | 140 | ||||
-rw-r--r-- | drivers/s390/char/monwriter.c | 98 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.c | 84 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.h | 12 | ||||
-rw-r--r-- | drivers/s390/char/sclp.c | 248 | ||||
-rw-r--r-- | drivers/s390/char/sclp.h | 23 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 42 | ||||
-rw-r--r-- | drivers/s390/char/sclp_con.c | 139 | ||||
-rw-r--r-- | drivers/s390/char/sclp_rw.c | 20 | ||||
-rw-r--r-- | drivers/s390/char/sclp_rw.h | 12 | ||||
-rw-r--r-- | drivers/s390/char/sclp_vt220.c | 118 | ||||
-rw-r--r-- | drivers/s390/char/tape.h | 3 | ||||
-rw-r--r-- | drivers/s390/char/tape_34xx.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/tape_3590.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/tape_core.c | 52 | ||||
-rw-r--r-- | drivers/s390/char/vmlogrdr.c | 27 | ||||
-rw-r--r-- | drivers/s390/char/vmur.c | 26 | ||||
-rw-r--r-- | drivers/s390/char/vmwatchdog.c | 81 |
21 files changed, 1089 insertions, 275 deletions
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 9ab06e0dad40..b79f31add39c 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c | |||
@@ -1,14 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/con3215.c | 2 | * 3215 line mode terminal driver. |
3 | * 3215 line mode terminal driver. | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corp. 1999, 2009 |
6 | * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), | ||
8 | * | 6 | * |
9 | * Updated: | 7 | * Updated: |
10 | * Aug-2000: Added tab support | 8 | * Aug-2000: Added tab support |
11 | * Dan Morrison, IBM Corporation (dmorriso@cse.buffalo.edu) | 9 | * Dan Morrison, IBM Corporation <dmorriso@cse.buffalo.edu> |
12 | */ | 10 | */ |
13 | 11 | ||
14 | #include <linux/module.h> | 12 | #include <linux/module.h> |
@@ -56,6 +54,7 @@ | |||
56 | #define RAW3215_CLOSING 32 /* set while in close process */ | 54 | #define RAW3215_CLOSING 32 /* set while in close process */ |
57 | #define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */ | 55 | #define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */ |
58 | #define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */ | 56 | #define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */ |
57 | #define RAW3215_FROZEN 256 /* set if 3215 is frozen for suspend */ | ||
59 | 58 | ||
60 | #define TAB_STOP_SIZE 8 /* tab stop size */ | 59 | #define TAB_STOP_SIZE 8 /* tab stop size */ |
61 | 60 | ||
@@ -111,8 +110,8 @@ static struct tty_driver *tty3215_driver; | |||
111 | /* | 110 | /* |
112 | * Get a request structure from the free list | 111 | * Get a request structure from the free list |
113 | */ | 112 | */ |
114 | static inline struct raw3215_req * | 113 | static inline struct raw3215_req *raw3215_alloc_req(void) |
115 | raw3215_alloc_req(void) { | 114 | { |
116 | struct raw3215_req *req; | 115 | struct raw3215_req *req; |
117 | unsigned long flags; | 116 | unsigned long flags; |
118 | 117 | ||
@@ -126,8 +125,8 @@ raw3215_alloc_req(void) { | |||
126 | /* | 125 | /* |
127 | * Put a request structure back to the free list | 126 | * Put a request structure back to the free list |
128 | */ | 127 | */ |
129 | static inline void | 128 | static inline void raw3215_free_req(struct raw3215_req *req) |
130 | raw3215_free_req(struct raw3215_req *req) { | 129 | { |
131 | unsigned long flags; | 130 | unsigned long flags; |
132 | 131 | ||
133 | if (req->type == RAW3215_FREE) | 132 | if (req->type == RAW3215_FREE) |
@@ -145,8 +144,7 @@ raw3215_free_req(struct raw3215_req *req) { | |||
145 | * because a 3215 terminal won't accept a new read before the old one is | 144 | * because a 3215 terminal won't accept a new read before the old one is |
146 | * completed. | 145 | * completed. |
147 | */ | 146 | */ |
148 | static void | 147 | static void raw3215_mk_read_req(struct raw3215_info *raw) |
149 | raw3215_mk_read_req(struct raw3215_info *raw) | ||
150 | { | 148 | { |
151 | struct raw3215_req *req; | 149 | struct raw3215_req *req; |
152 | struct ccw1 *ccw; | 150 | struct ccw1 *ccw; |
@@ -174,8 +172,7 @@ raw3215_mk_read_req(struct raw3215_info *raw) | |||
174 | * buffer to the 3215 device. If a queued write exists it is replaced by | 172 | * buffer to the 3215 device. If a queued write exists it is replaced by |
175 | * the new, probably lengthened request. | 173 | * the new, probably lengthened request. |
176 | */ | 174 | */ |
177 | static void | 175 | static void raw3215_mk_write_req(struct raw3215_info *raw) |
178 | raw3215_mk_write_req(struct raw3215_info *raw) | ||
179 | { | 176 | { |
180 | struct raw3215_req *req; | 177 | struct raw3215_req *req; |
181 | struct ccw1 *ccw; | 178 | struct ccw1 *ccw; |
@@ -251,8 +248,7 @@ raw3215_mk_write_req(struct raw3215_info *raw) | |||
251 | /* | 248 | /* |
252 | * Start a read or a write request | 249 | * Start a read or a write request |
253 | */ | 250 | */ |
254 | static void | 251 | static void raw3215_start_io(struct raw3215_info *raw) |
255 | raw3215_start_io(struct raw3215_info *raw) | ||
256 | { | 252 | { |
257 | struct raw3215_req *req; | 253 | struct raw3215_req *req; |
258 | int res; | 254 | int res; |
@@ -290,8 +286,7 @@ raw3215_start_io(struct raw3215_info *raw) | |||
290 | /* | 286 | /* |
291 | * Function to start a delayed output after RAW3215_TIMEOUT seconds | 287 | * Function to start a delayed output after RAW3215_TIMEOUT seconds |
292 | */ | 288 | */ |
293 | static void | 289 | static void raw3215_timeout(unsigned long __data) |
294 | raw3215_timeout(unsigned long __data) | ||
295 | { | 290 | { |
296 | struct raw3215_info *raw = (struct raw3215_info *) __data; | 291 | struct raw3215_info *raw = (struct raw3215_info *) __data; |
297 | unsigned long flags; | 292 | unsigned long flags; |
@@ -300,8 +295,10 @@ raw3215_timeout(unsigned long __data) | |||
300 | if (raw->flags & RAW3215_TIMER_RUNS) { | 295 | if (raw->flags & RAW3215_TIMER_RUNS) { |
301 | del_timer(&raw->timer); | 296 | del_timer(&raw->timer); |
302 | raw->flags &= ~RAW3215_TIMER_RUNS; | 297 | raw->flags &= ~RAW3215_TIMER_RUNS; |
303 | raw3215_mk_write_req(raw); | 298 | if (!(raw->flags & RAW3215_FROZEN)) { |
304 | raw3215_start_io(raw); | 299 | raw3215_mk_write_req(raw); |
300 | raw3215_start_io(raw); | ||
301 | } | ||
305 | } | 302 | } |
306 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); | 303 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); |
307 | } | 304 | } |
@@ -312,10 +309,9 @@ raw3215_timeout(unsigned long __data) | |||
312 | * amount of data is bigger than RAW3215_MIN_WRITE. If a write is not | 309 | * amount of data is bigger than RAW3215_MIN_WRITE. If a write is not |
313 | * done immediately a timer is started with a delay of RAW3215_TIMEOUT. | 310 | * done immediately a timer is started with a delay of RAW3215_TIMEOUT. |
314 | */ | 311 | */ |
315 | static inline void | 312 | static inline void raw3215_try_io(struct raw3215_info *raw) |
316 | raw3215_try_io(struct raw3215_info *raw) | ||
317 | { | 313 | { |
318 | if (!(raw->flags & RAW3215_ACTIVE)) | 314 | if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FROZEN)) |
319 | return; | 315 | return; |
320 | if (raw->queued_read != NULL) | 316 | if (raw->queued_read != NULL) |
321 | raw3215_start_io(raw); | 317 | raw3215_start_io(raw); |
@@ -359,8 +355,8 @@ static void raw3215_next_io(struct raw3215_info *raw) | |||
359 | /* | 355 | /* |
360 | * Interrupt routine, called from common io layer | 356 | * Interrupt routine, called from common io layer |
361 | */ | 357 | */ |
362 | static void | 358 | static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, |
363 | raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | 359 | struct irb *irb) |
364 | { | 360 | { |
365 | struct raw3215_info *raw; | 361 | struct raw3215_info *raw; |
366 | struct raw3215_req *req; | 362 | struct raw3215_req *req; |
@@ -459,14 +455,40 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | |||
459 | } | 455 | } |
460 | 456 | ||
461 | /* | 457 | /* |
458 | * Drop the oldest line from the output buffer. | ||
459 | */ | ||
460 | static void raw3215_drop_line(struct raw3215_info *raw) | ||
461 | { | ||
462 | int ix; | ||
463 | char ch; | ||
464 | |||
465 | BUG_ON(raw->written != 0); | ||
466 | ix = (raw->head - raw->count) & (RAW3215_BUFFER_SIZE - 1); | ||
467 | while (raw->count > 0) { | ||
468 | ch = raw->buffer[ix]; | ||
469 | ix = (ix + 1) & (RAW3215_BUFFER_SIZE - 1); | ||
470 | raw->count--; | ||
471 | if (ch == 0x15) | ||
472 | break; | ||
473 | } | ||
474 | raw->head = ix; | ||
475 | } | ||
476 | |||
477 | /* | ||
462 | * Wait until length bytes are available int the output buffer. | 478 | * Wait until length bytes are available int the output buffer. |
463 | * Has to be called with the s390irq lock held. Can be called | 479 | * Has to be called with the s390irq lock held. Can be called |
464 | * disabled. | 480 | * disabled. |
465 | */ | 481 | */ |
466 | static void | 482 | static void raw3215_make_room(struct raw3215_info *raw, unsigned int length) |
467 | raw3215_make_room(struct raw3215_info *raw, unsigned int length) | ||
468 | { | 483 | { |
469 | while (RAW3215_BUFFER_SIZE - raw->count < length) { | 484 | while (RAW3215_BUFFER_SIZE - raw->count < length) { |
485 | /* While console is frozen for suspend we have no other | ||
486 | * choice but to drop message from the buffer to make | ||
487 | * room for even more messages. */ | ||
488 | if (raw->flags & RAW3215_FROZEN) { | ||
489 | raw3215_drop_line(raw); | ||
490 | continue; | ||
491 | } | ||
470 | /* there might be a request pending */ | 492 | /* there might be a request pending */ |
471 | raw->flags |= RAW3215_FLUSHING; | 493 | raw->flags |= RAW3215_FLUSHING; |
472 | raw3215_mk_write_req(raw); | 494 | raw3215_mk_write_req(raw); |
@@ -488,8 +510,8 @@ raw3215_make_room(struct raw3215_info *raw, unsigned int length) | |||
488 | /* | 510 | /* |
489 | * String write routine for 3215 devices | 511 | * String write routine for 3215 devices |
490 | */ | 512 | */ |
491 | static void | 513 | static void raw3215_write(struct raw3215_info *raw, const char *str, |
492 | raw3215_write(struct raw3215_info *raw, const char *str, unsigned int length) | 514 | unsigned int length) |
493 | { | 515 | { |
494 | unsigned long flags; | 516 | unsigned long flags; |
495 | int c, count; | 517 | int c, count; |
@@ -529,8 +551,7 @@ raw3215_write(struct raw3215_info *raw, const char *str, unsigned int length) | |||
529 | /* | 551 | /* |
530 | * Put character routine for 3215 devices | 552 | * Put character routine for 3215 devices |
531 | */ | 553 | */ |
532 | static void | 554 | static void raw3215_putchar(struct raw3215_info *raw, unsigned char ch) |
533 | raw3215_putchar(struct raw3215_info *raw, unsigned char ch) | ||
534 | { | 555 | { |
535 | unsigned long flags; | 556 | unsigned long flags; |
536 | unsigned int length, i; | 557 | unsigned int length, i; |
@@ -566,8 +587,7 @@ raw3215_putchar(struct raw3215_info *raw, unsigned char ch) | |||
566 | * Flush routine, it simply sets the flush flag and tries to start | 587 | * Flush routine, it simply sets the flush flag and tries to start |
567 | * pending IO. | 588 | * pending IO. |
568 | */ | 589 | */ |
569 | static void | 590 | static void raw3215_flush_buffer(struct raw3215_info *raw) |
570 | raw3215_flush_buffer(struct raw3215_info *raw) | ||
571 | { | 591 | { |
572 | unsigned long flags; | 592 | unsigned long flags; |
573 | 593 | ||
@@ -583,8 +603,7 @@ raw3215_flush_buffer(struct raw3215_info *raw) | |||
583 | /* | 603 | /* |
584 | * Fire up a 3215 device. | 604 | * Fire up a 3215 device. |
585 | */ | 605 | */ |
586 | static int | 606 | static int raw3215_startup(struct raw3215_info *raw) |
587 | raw3215_startup(struct raw3215_info *raw) | ||
588 | { | 607 | { |
589 | unsigned long flags; | 608 | unsigned long flags; |
590 | 609 | ||
@@ -602,8 +621,7 @@ raw3215_startup(struct raw3215_info *raw) | |||
602 | /* | 621 | /* |
603 | * Shutdown a 3215 device. | 622 | * Shutdown a 3215 device. |
604 | */ | 623 | */ |
605 | static void | 624 | static void raw3215_shutdown(struct raw3215_info *raw) |
606 | raw3215_shutdown(struct raw3215_info *raw) | ||
607 | { | 625 | { |
608 | DECLARE_WAITQUEUE(wait, current); | 626 | DECLARE_WAITQUEUE(wait, current); |
609 | unsigned long flags; | 627 | unsigned long flags; |
@@ -628,8 +646,7 @@ raw3215_shutdown(struct raw3215_info *raw) | |||
628 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); | 646 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); |
629 | } | 647 | } |
630 | 648 | ||
631 | static int | 649 | static int raw3215_probe (struct ccw_device *cdev) |
632 | raw3215_probe (struct ccw_device *cdev) | ||
633 | { | 650 | { |
634 | struct raw3215_info *raw; | 651 | struct raw3215_info *raw; |
635 | int line; | 652 | int line; |
@@ -675,8 +692,7 @@ raw3215_probe (struct ccw_device *cdev) | |||
675 | return 0; | 692 | return 0; |
676 | } | 693 | } |
677 | 694 | ||
678 | static void | 695 | static void raw3215_remove (struct ccw_device *cdev) |
679 | raw3215_remove (struct ccw_device *cdev) | ||
680 | { | 696 | { |
681 | struct raw3215_info *raw; | 697 | struct raw3215_info *raw; |
682 | 698 | ||
@@ -689,8 +705,7 @@ raw3215_remove (struct ccw_device *cdev) | |||
689 | } | 705 | } |
690 | } | 706 | } |
691 | 707 | ||
692 | static int | 708 | static int raw3215_set_online (struct ccw_device *cdev) |
693 | raw3215_set_online (struct ccw_device *cdev) | ||
694 | { | 709 | { |
695 | struct raw3215_info *raw; | 710 | struct raw3215_info *raw; |
696 | 711 | ||
@@ -701,8 +716,7 @@ raw3215_set_online (struct ccw_device *cdev) | |||
701 | return raw3215_startup(raw); | 716 | return raw3215_startup(raw); |
702 | } | 717 | } |
703 | 718 | ||
704 | static int | 719 | static int raw3215_set_offline (struct ccw_device *cdev) |
705 | raw3215_set_offline (struct ccw_device *cdev) | ||
706 | { | 720 | { |
707 | struct raw3215_info *raw; | 721 | struct raw3215_info *raw; |
708 | 722 | ||
@@ -715,6 +729,36 @@ raw3215_set_offline (struct ccw_device *cdev) | |||
715 | return 0; | 729 | return 0; |
716 | } | 730 | } |
717 | 731 | ||
732 | static int raw3215_pm_stop(struct ccw_device *cdev) | ||
733 | { | ||
734 | struct raw3215_info *raw; | ||
735 | unsigned long flags; | ||
736 | |||
737 | /* Empty the output buffer, then prevent new I/O. */ | ||
738 | raw = cdev->dev.driver_data; | ||
739 | spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); | ||
740 | raw3215_make_room(raw, RAW3215_BUFFER_SIZE); | ||
741 | raw->flags |= RAW3215_FROZEN; | ||
742 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); | ||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | static int raw3215_pm_start(struct ccw_device *cdev) | ||
747 | { | ||
748 | struct raw3215_info *raw; | ||
749 | unsigned long flags; | ||
750 | |||
751 | /* Allow I/O again and flush output buffer. */ | ||
752 | raw = cdev->dev.driver_data; | ||
753 | spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); | ||
754 | raw->flags &= ~RAW3215_FROZEN; | ||
755 | raw->flags |= RAW3215_FLUSHING; | ||
756 | raw3215_try_io(raw); | ||
757 | raw->flags &= ~RAW3215_FLUSHING; | ||
758 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); | ||
759 | return 0; | ||
760 | } | ||
761 | |||
718 | static struct ccw_device_id raw3215_id[] = { | 762 | static struct ccw_device_id raw3215_id[] = { |
719 | { CCW_DEVICE(0x3215, 0) }, | 763 | { CCW_DEVICE(0x3215, 0) }, |
720 | { /* end of list */ }, | 764 | { /* end of list */ }, |
@@ -728,14 +772,17 @@ static struct ccw_driver raw3215_ccw_driver = { | |||
728 | .remove = &raw3215_remove, | 772 | .remove = &raw3215_remove, |
729 | .set_online = &raw3215_set_online, | 773 | .set_online = &raw3215_set_online, |
730 | .set_offline = &raw3215_set_offline, | 774 | .set_offline = &raw3215_set_offline, |
775 | .freeze = &raw3215_pm_stop, | ||
776 | .thaw = &raw3215_pm_start, | ||
777 | .restore = &raw3215_pm_start, | ||
731 | }; | 778 | }; |
732 | 779 | ||
733 | #ifdef CONFIG_TN3215_CONSOLE | 780 | #ifdef CONFIG_TN3215_CONSOLE |
734 | /* | 781 | /* |
735 | * Write a string to the 3215 console | 782 | * Write a string to the 3215 console |
736 | */ | 783 | */ |
737 | static void | 784 | static void con3215_write(struct console *co, const char *str, |
738 | con3215_write(struct console *co, const char *str, unsigned int count) | 785 | unsigned int count) |
739 | { | 786 | { |
740 | struct raw3215_info *raw; | 787 | struct raw3215_info *raw; |
741 | int i; | 788 | int i; |
@@ -768,13 +815,17 @@ static struct tty_driver *con3215_device(struct console *c, int *index) | |||
768 | * panic() calls con3215_flush through a panic_notifier | 815 | * panic() calls con3215_flush through a panic_notifier |
769 | * before the system enters a disabled, endless loop. | 816 | * before the system enters a disabled, endless loop. |
770 | */ | 817 | */ |
771 | static void | 818 | static void con3215_flush(void) |
772 | con3215_flush(void) | ||
773 | { | 819 | { |
774 | struct raw3215_info *raw; | 820 | struct raw3215_info *raw; |
775 | unsigned long flags; | 821 | unsigned long flags; |
776 | 822 | ||
777 | raw = raw3215[0]; /* console 3215 is the first one */ | 823 | raw = raw3215[0]; /* console 3215 is the first one */ |
824 | if (raw->flags & RAW3215_FROZEN) | ||
825 | /* The console is still frozen for suspend. */ | ||
826 | if (ccw_device_force_console()) | ||
827 | /* Forcing didn't work, no panic message .. */ | ||
828 | return; | ||
778 | spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); | 829 | spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); |
779 | raw3215_make_room(raw, RAW3215_BUFFER_SIZE); | 830 | raw3215_make_room(raw, RAW3215_BUFFER_SIZE); |
780 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); | 831 | spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); |
@@ -811,8 +862,7 @@ static struct console con3215 = { | |||
811 | * 3215 console initialization code called from console_init(). | 862 | * 3215 console initialization code called from console_init(). |
812 | * NOTE: This is called before kmalloc is available. | 863 | * NOTE: This is called before kmalloc is available. |
813 | */ | 864 | */ |
814 | static int __init | 865 | static int __init con3215_init(void) |
815 | con3215_init(void) | ||
816 | { | 866 | { |
817 | struct ccw_device *cdev; | 867 | struct ccw_device *cdev; |
818 | struct raw3215_info *raw; | 868 | struct raw3215_info *raw; |
@@ -875,8 +925,7 @@ console_initcall(con3215_init); | |||
875 | * | 925 | * |
876 | * This routine is called whenever a 3215 tty is opened. | 926 | * This routine is called whenever a 3215 tty is opened. |
877 | */ | 927 | */ |
878 | static int | 928 | static int tty3215_open(struct tty_struct *tty, struct file * filp) |
879 | tty3215_open(struct tty_struct *tty, struct file * filp) | ||
880 | { | 929 | { |
881 | struct raw3215_info *raw; | 930 | struct raw3215_info *raw; |
882 | int retval, line; | 931 | int retval, line; |
@@ -909,8 +958,7 @@ tty3215_open(struct tty_struct *tty, struct file * filp) | |||
909 | * This routine is called when the 3215 tty is closed. We wait | 958 | * This routine is called when the 3215 tty is closed. We wait |
910 | * for the remaining request to be completed. Then we clean up. | 959 | * for the remaining request to be completed. Then we clean up. |
911 | */ | 960 | */ |
912 | static void | 961 | static void tty3215_close(struct tty_struct *tty, struct file * filp) |
913 | tty3215_close(struct tty_struct *tty, struct file * filp) | ||
914 | { | 962 | { |
915 | struct raw3215_info *raw; | 963 | struct raw3215_info *raw; |
916 | 964 | ||
@@ -927,8 +975,7 @@ tty3215_close(struct tty_struct *tty, struct file * filp) | |||
927 | /* | 975 | /* |
928 | * Returns the amount of free space in the output buffer. | 976 | * Returns the amount of free space in the output buffer. |
929 | */ | 977 | */ |
930 | static int | 978 | static int tty3215_write_room(struct tty_struct *tty) |
931 | tty3215_write_room(struct tty_struct *tty) | ||
932 | { | 979 | { |
933 | struct raw3215_info *raw; | 980 | struct raw3215_info *raw; |
934 | 981 | ||
@@ -944,9 +991,8 @@ tty3215_write_room(struct tty_struct *tty) | |||
944 | /* | 991 | /* |
945 | * String write routine for 3215 ttys | 992 | * String write routine for 3215 ttys |
946 | */ | 993 | */ |
947 | static int | 994 | static int tty3215_write(struct tty_struct * tty, |
948 | tty3215_write(struct tty_struct * tty, | 995 | const unsigned char *buf, int count) |
949 | const unsigned char *buf, int count) | ||
950 | { | 996 | { |
951 | struct raw3215_info *raw; | 997 | struct raw3215_info *raw; |
952 | 998 | ||
@@ -960,8 +1006,7 @@ tty3215_write(struct tty_struct * tty, | |||
960 | /* | 1006 | /* |
961 | * Put character routine for 3215 ttys | 1007 | * Put character routine for 3215 ttys |
962 | */ | 1008 | */ |
963 | static int | 1009 | static int tty3215_put_char(struct tty_struct *tty, unsigned char ch) |
964 | tty3215_put_char(struct tty_struct *tty, unsigned char ch) | ||
965 | { | 1010 | { |
966 | struct raw3215_info *raw; | 1011 | struct raw3215_info *raw; |
967 | 1012 | ||
@@ -972,16 +1017,14 @@ tty3215_put_char(struct tty_struct *tty, unsigned char ch) | |||
972 | return 1; | 1017 | return 1; |
973 | } | 1018 | } |
974 | 1019 | ||
975 | static void | 1020 | static void tty3215_flush_chars(struct tty_struct *tty) |
976 | tty3215_flush_chars(struct tty_struct *tty) | ||
977 | { | 1021 | { |
978 | } | 1022 | } |
979 | 1023 | ||
980 | /* | 1024 | /* |
981 | * Returns the number of characters in the output buffer | 1025 | * Returns the number of characters in the output buffer |
982 | */ | 1026 | */ |
983 | static int | 1027 | static int tty3215_chars_in_buffer(struct tty_struct *tty) |
984 | tty3215_chars_in_buffer(struct tty_struct *tty) | ||
985 | { | 1028 | { |
986 | struct raw3215_info *raw; | 1029 | struct raw3215_info *raw; |
987 | 1030 | ||
@@ -989,8 +1032,7 @@ tty3215_chars_in_buffer(struct tty_struct *tty) | |||
989 | return raw->count; | 1032 | return raw->count; |
990 | } | 1033 | } |
991 | 1034 | ||
992 | static void | 1035 | static void tty3215_flush_buffer(struct tty_struct *tty) |
993 | tty3215_flush_buffer(struct tty_struct *tty) | ||
994 | { | 1036 | { |
995 | struct raw3215_info *raw; | 1037 | struct raw3215_info *raw; |
996 | 1038 | ||
@@ -1002,9 +1044,8 @@ tty3215_flush_buffer(struct tty_struct *tty) | |||
1002 | /* | 1044 | /* |
1003 | * Currently we don't have any io controls for 3215 ttys | 1045 | * Currently we don't have any io controls for 3215 ttys |
1004 | */ | 1046 | */ |
1005 | static int | 1047 | static int tty3215_ioctl(struct tty_struct *tty, struct file * file, |
1006 | tty3215_ioctl(struct tty_struct *tty, struct file * file, | 1048 | unsigned int cmd, unsigned long arg) |
1007 | unsigned int cmd, unsigned long arg) | ||
1008 | { | 1049 | { |
1009 | if (tty->flags & (1 << TTY_IO_ERROR)) | 1050 | if (tty->flags & (1 << TTY_IO_ERROR)) |
1010 | return -EIO; | 1051 | return -EIO; |
@@ -1019,8 +1060,7 @@ tty3215_ioctl(struct tty_struct *tty, struct file * file, | |||
1019 | /* | 1060 | /* |
1020 | * Disable reading from a 3215 tty | 1061 | * Disable reading from a 3215 tty |
1021 | */ | 1062 | */ |
1022 | static void | 1063 | static void tty3215_throttle(struct tty_struct * tty) |
1023 | tty3215_throttle(struct tty_struct * tty) | ||
1024 | { | 1064 | { |
1025 | struct raw3215_info *raw; | 1065 | struct raw3215_info *raw; |
1026 | 1066 | ||
@@ -1031,8 +1071,7 @@ tty3215_throttle(struct tty_struct * tty) | |||
1031 | /* | 1071 | /* |
1032 | * Enable reading from a 3215 tty | 1072 | * Enable reading from a 3215 tty |
1033 | */ | 1073 | */ |
1034 | static void | 1074 | static void tty3215_unthrottle(struct tty_struct * tty) |
1035 | tty3215_unthrottle(struct tty_struct * tty) | ||
1036 | { | 1075 | { |
1037 | struct raw3215_info *raw; | 1076 | struct raw3215_info *raw; |
1038 | unsigned long flags; | 1077 | unsigned long flags; |
@@ -1049,8 +1088,7 @@ tty3215_unthrottle(struct tty_struct * tty) | |||
1049 | /* | 1088 | /* |
1050 | * Disable writing to a 3215 tty | 1089 | * Disable writing to a 3215 tty |
1051 | */ | 1090 | */ |
1052 | static void | 1091 | static void tty3215_stop(struct tty_struct *tty) |
1053 | tty3215_stop(struct tty_struct *tty) | ||
1054 | { | 1092 | { |
1055 | struct raw3215_info *raw; | 1093 | struct raw3215_info *raw; |
1056 | 1094 | ||
@@ -1061,8 +1099,7 @@ tty3215_stop(struct tty_struct *tty) | |||
1061 | /* | 1099 | /* |
1062 | * Enable writing to a 3215 tty | 1100 | * Enable writing to a 3215 tty |
1063 | */ | 1101 | */ |
1064 | static void | 1102 | static void tty3215_start(struct tty_struct *tty) |
1065 | tty3215_start(struct tty_struct *tty) | ||
1066 | { | 1103 | { |
1067 | struct raw3215_info *raw; | 1104 | struct raw3215_info *raw; |
1068 | unsigned long flags; | 1105 | unsigned long flags; |
@@ -1096,8 +1133,7 @@ static const struct tty_operations tty3215_ops = { | |||
1096 | * 3215 tty registration code called from tty_init(). | 1133 | * 3215 tty registration code called from tty_init(). |
1097 | * Most kernel services (incl. kmalloc) are available at this poimt. | 1134 | * Most kernel services (incl. kmalloc) are available at this poimt. |
1098 | */ | 1135 | */ |
1099 | static int __init | 1136 | static int __init tty3215_init(void) |
1100 | tty3215_init(void) | ||
1101 | { | 1137 | { |
1102 | struct tty_driver *driver; | 1138 | struct tty_driver *driver; |
1103 | int ret; | 1139 | int ret; |
@@ -1142,8 +1178,7 @@ tty3215_init(void) | |||
1142 | return 0; | 1178 | return 0; |
1143 | } | 1179 | } |
1144 | 1180 | ||
1145 | static void __exit | 1181 | static void __exit tty3215_exit(void) |
1146 | tty3215_exit(void) | ||
1147 | { | 1182 | { |
1148 | tty_unregister_driver(tty3215_driver); | 1183 | tty_unregister_driver(tty3215_driver); |
1149 | put_tty_driver(tty3215_driver); | 1184 | put_tty_driver(tty3215_driver); |
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index ed5396dae58e..44d02e371c04 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/con3270.c | 2 | * IBM/3270 Driver - console view. |
3 | * IBM/3270 Driver - console view. | ||
4 | * | 3 | * |
5 | * Author(s): | 4 | * Author(s): |
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | 5 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) |
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | 7 | * Copyright IBM Corp. 2003, 2009 |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/bootmem.h> | 10 | #include <linux/bootmem.h> |
@@ -530,6 +529,7 @@ con3270_flush(void) | |||
530 | cp = condev; | 529 | cp = condev; |
531 | if (!cp->view.dev) | 530 | if (!cp->view.dev) |
532 | return; | 531 | return; |
532 | raw3270_pm_unfreeze(&cp->view); | ||
533 | spin_lock_irqsave(&cp->view.lock, flags); | 533 | spin_lock_irqsave(&cp->view.lock, flags); |
534 | con3270_wait_write(cp); | 534 | con3270_wait_write(cp); |
535 | cp->nr_up = 0; | 535 | cp->nr_up = 0; |
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 40759c33477d..097d3846a828 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/fs3270.c | 2 | * IBM/3270 Driver - fullscreen driver. |
3 | * IBM/3270 Driver - fullscreen driver. | ||
4 | * | 3 | * |
5 | * Author(s): | 4 | * Author(s): |
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | 5 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) |
7 | * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Rewritten for 2.5/2.6 by Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | 7 | * Copyright IBM Corp. 2003, 2009 |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/bootmem.h> | 10 | #include <linux/bootmem.h> |
@@ -399,6 +398,11 @@ fs3270_free_view(struct raw3270_view *view) | |||
399 | static void | 398 | static void |
400 | fs3270_release(struct raw3270_view *view) | 399 | fs3270_release(struct raw3270_view *view) |
401 | { | 400 | { |
401 | struct fs3270 *fp; | ||
402 | |||
403 | fp = (struct fs3270 *) view; | ||
404 | if (fp->fs_pid) | ||
405 | kill_pid(fp->fs_pid, SIGHUP, 1); | ||
402 | } | 406 | } |
403 | 407 | ||
404 | /* View to a 3270 device. Can be console, tty or fullscreen. */ | 408 | /* View to a 3270 device. Can be console, tty or fullscreen. */ |
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 97e63cf46944..75a8831eebbc 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c | |||
@@ -1,10 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/monreader.c | ||
3 | * | ||
4 | * Character device driver for reading z/VM *MONITOR service records. | 2 | * Character device driver for reading z/VM *MONITOR service records. |
5 | * | 3 | * |
6 | * Copyright IBM Corp. 2004, 2008 | 4 | * Copyright IBM Corp. 2004, 2009 |
7 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | 5 | * |
6 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | ||
8 | */ | 7 | */ |
9 | 8 | ||
10 | #define KMSG_COMPONENT "monreader" | 9 | #define KMSG_COMPONENT "monreader" |
@@ -22,6 +21,7 @@ | |||
22 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
23 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
24 | #include <linux/poll.h> | 23 | #include <linux/poll.h> |
24 | #include <linux/device.h> | ||
25 | #include <net/iucv/iucv.h> | 25 | #include <net/iucv/iucv.h> |
26 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
27 | #include <asm/ebcdic.h> | 27 | #include <asm/ebcdic.h> |
@@ -78,6 +78,7 @@ static u8 user_data_sever[16] = { | |||
78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
79 | }; | 79 | }; |
80 | 80 | ||
81 | static struct device *monreader_device; | ||
81 | 82 | ||
82 | /****************************************************************************** | 83 | /****************************************************************************** |
83 | * helper functions * | 84 | * helper functions * |
@@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp) | |||
319 | goto out_path; | 320 | goto out_path; |
320 | } | 321 | } |
321 | filp->private_data = monpriv; | 322 | filp->private_data = monpriv; |
323 | monreader_device->driver_data = monpriv; | ||
322 | unlock_kernel(); | 324 | unlock_kernel(); |
323 | return nonseekable_open(inode, filp); | 325 | return nonseekable_open(inode, filp); |
324 | 326 | ||
325 | out_path: | 327 | out_path: |
326 | kfree(monpriv->path); | 328 | iucv_path_free(monpriv->path); |
327 | out_priv: | 329 | out_priv: |
328 | mon_free_mem(monpriv); | 330 | mon_free_mem(monpriv); |
329 | out_use: | 331 | out_use: |
@@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp) | |||
341 | /* | 343 | /* |
342 | * Close IUCV connection and unregister | 344 | * Close IUCV connection and unregister |
343 | */ | 345 | */ |
344 | rc = iucv_path_sever(monpriv->path, user_data_sever); | 346 | if (monpriv->path) { |
345 | if (rc) | 347 | rc = iucv_path_sever(monpriv->path, user_data_sever); |
346 | pr_warning("Disconnecting the z/VM *MONITOR system service " | 348 | if (rc) |
347 | "failed with rc=%i\n", rc); | 349 | pr_warning("Disconnecting the z/VM *MONITOR system " |
350 | "service failed with rc=%i\n", rc); | ||
351 | iucv_path_free(monpriv->path); | ||
352 | } | ||
348 | 353 | ||
349 | atomic_set(&monpriv->iucv_severed, 0); | 354 | atomic_set(&monpriv->iucv_severed, 0); |
350 | atomic_set(&monpriv->iucv_connected, 0); | 355 | atomic_set(&monpriv->iucv_connected, 0); |
@@ -452,6 +457,94 @@ static struct miscdevice mon_dev = { | |||
452 | .minor = MISC_DYNAMIC_MINOR, | 457 | .minor = MISC_DYNAMIC_MINOR, |
453 | }; | 458 | }; |
454 | 459 | ||
460 | |||
461 | /****************************************************************************** | ||
462 | * suspend / resume * | ||
463 | *****************************************************************************/ | ||
464 | static int monreader_freeze(struct device *dev) | ||
465 | { | ||
466 | struct mon_private *monpriv = dev->driver_data; | ||
467 | int rc; | ||
468 | |||
469 | if (!monpriv) | ||
470 | return 0; | ||
471 | if (monpriv->path) { | ||
472 | rc = iucv_path_sever(monpriv->path, user_data_sever); | ||
473 | if (rc) | ||
474 | pr_warning("Disconnecting the z/VM *MONITOR system " | ||
475 | "service failed with rc=%i\n", rc); | ||
476 | iucv_path_free(monpriv->path); | ||
477 | } | ||
478 | atomic_set(&monpriv->iucv_severed, 0); | ||
479 | atomic_set(&monpriv->iucv_connected, 0); | ||
480 | atomic_set(&monpriv->read_ready, 0); | ||
481 | atomic_set(&monpriv->msglim_count, 0); | ||
482 | monpriv->write_index = 0; | ||
483 | monpriv->read_index = 0; | ||
484 | monpriv->path = NULL; | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int monreader_thaw(struct device *dev) | ||
489 | { | ||
490 | struct mon_private *monpriv = dev->driver_data; | ||
491 | int rc; | ||
492 | |||
493 | if (!monpriv) | ||
494 | return 0; | ||
495 | rc = -ENOMEM; | ||
496 | monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL); | ||
497 | if (!monpriv->path) | ||
498 | goto out; | ||
499 | rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler, | ||
500 | MON_SERVICE, NULL, user_data_connect, monpriv); | ||
501 | if (rc) { | ||
502 | pr_err("Connecting to the z/VM *MONITOR system service " | ||
503 | "failed with rc=%i\n", rc); | ||
504 | goto out_path; | ||
505 | } | ||
506 | wait_event(mon_conn_wait_queue, | ||
507 | atomic_read(&monpriv->iucv_connected) || | ||
508 | atomic_read(&monpriv->iucv_severed)); | ||
509 | if (atomic_read(&monpriv->iucv_severed)) | ||
510 | goto out_path; | ||
511 | return 0; | ||
512 | out_path: | ||
513 | rc = -EIO; | ||
514 | iucv_path_free(monpriv->path); | ||
515 | monpriv->path = NULL; | ||
516 | out: | ||
517 | atomic_set(&monpriv->iucv_severed, 1); | ||
518 | return rc; | ||
519 | } | ||
520 | |||
521 | static int monreader_restore(struct device *dev) | ||
522 | { | ||
523 | int rc; | ||
524 | |||
525 | segment_unload(mon_dcss_name); | ||
526 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, | ||
527 | &mon_dcss_start, &mon_dcss_end); | ||
528 | if (rc < 0) { | ||
529 | segment_warning(rc, mon_dcss_name); | ||
530 | panic("fatal monreader resume error: no monitor dcss\n"); | ||
531 | } | ||
532 | return monreader_thaw(dev); | ||
533 | } | ||
534 | |||
535 | static struct dev_pm_ops monreader_pm_ops = { | ||
536 | .freeze = monreader_freeze, | ||
537 | .thaw = monreader_thaw, | ||
538 | .restore = monreader_restore, | ||
539 | }; | ||
540 | |||
541 | static struct device_driver monreader_driver = { | ||
542 | .name = "monreader", | ||
543 | .bus = &iucv_bus, | ||
544 | .pm = &monreader_pm_ops, | ||
545 | }; | ||
546 | |||
547 | |||
455 | /****************************************************************************** | 548 | /****************************************************************************** |
456 | * module init/exit * | 549 | * module init/exit * |
457 | *****************************************************************************/ | 550 | *****************************************************************************/ |
@@ -475,16 +568,33 @@ static int __init mon_init(void) | |||
475 | return rc; | 568 | return rc; |
476 | } | 569 | } |
477 | 570 | ||
571 | rc = driver_register(&monreader_driver); | ||
572 | if (rc) | ||
573 | goto out_iucv; | ||
574 | monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
575 | if (!monreader_device) | ||
576 | goto out_driver; | ||
577 | dev_set_name(monreader_device, "monreader-dev"); | ||
578 | monreader_device->bus = &iucv_bus; | ||
579 | monreader_device->parent = iucv_root; | ||
580 | monreader_device->driver = &monreader_driver; | ||
581 | monreader_device->release = (void (*)(struct device *))kfree; | ||
582 | rc = device_register(monreader_device); | ||
583 | if (rc) { | ||
584 | kfree(monreader_device); | ||
585 | goto out_driver; | ||
586 | } | ||
587 | |||
478 | rc = segment_type(mon_dcss_name); | 588 | rc = segment_type(mon_dcss_name); |
479 | if (rc < 0) { | 589 | if (rc < 0) { |
480 | segment_warning(rc, mon_dcss_name); | 590 | segment_warning(rc, mon_dcss_name); |
481 | goto out_iucv; | 591 | goto out_device; |
482 | } | 592 | } |
483 | if (rc != SEG_TYPE_SC) { | 593 | if (rc != SEG_TYPE_SC) { |
484 | pr_err("The specified *MONITOR DCSS %s does not have the " | 594 | pr_err("The specified *MONITOR DCSS %s does not have the " |
485 | "required type SC\n", mon_dcss_name); | 595 | "required type SC\n", mon_dcss_name); |
486 | rc = -EINVAL; | 596 | rc = -EINVAL; |
487 | goto out_iucv; | 597 | goto out_device; |
488 | } | 598 | } |
489 | 599 | ||
490 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, | 600 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, |
@@ -492,7 +602,7 @@ static int __init mon_init(void) | |||
492 | if (rc < 0) { | 602 | if (rc < 0) { |
493 | segment_warning(rc, mon_dcss_name); | 603 | segment_warning(rc, mon_dcss_name); |
494 | rc = -EINVAL; | 604 | rc = -EINVAL; |
495 | goto out_iucv; | 605 | goto out_device; |
496 | } | 606 | } |
497 | dcss_mkname(mon_dcss_name, &user_data_connect[8]); | 607 | dcss_mkname(mon_dcss_name, &user_data_connect[8]); |
498 | 608 | ||
@@ -503,6 +613,10 @@ static int __init mon_init(void) | |||
503 | 613 | ||
504 | out: | 614 | out: |
505 | segment_unload(mon_dcss_name); | 615 | segment_unload(mon_dcss_name); |
616 | out_device: | ||
617 | device_unregister(monreader_device); | ||
618 | out_driver: | ||
619 | driver_unregister(&monreader_driver); | ||
506 | out_iucv: | 620 | out_iucv: |
507 | iucv_unregister(&monreader_iucv_handler, 1); | 621 | iucv_unregister(&monreader_iucv_handler, 1); |
508 | return rc; | 622 | return rc; |
@@ -512,6 +626,8 @@ static void __exit mon_exit(void) | |||
512 | { | 626 | { |
513 | segment_unload(mon_dcss_name); | 627 | segment_unload(mon_dcss_name); |
514 | WARN_ON(misc_deregister(&mon_dev) != 0); | 628 | WARN_ON(misc_deregister(&mon_dev) != 0); |
629 | device_unregister(monreader_device); | ||
630 | driver_unregister(&monreader_driver); | ||
515 | iucv_unregister(&monreader_iucv_handler, 1); | 631 | iucv_unregister(&monreader_iucv_handler, 1); |
516 | return; | 632 | return; |
517 | } | 633 | } |
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index c7d7483bab9a..66fb8eba93f4 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c | |||
@@ -1,9 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/monwriter.c | ||
3 | * | ||
4 | * Character device driver for writing z/VM *MONITOR service records. | 2 | * Character device driver for writing z/VM *MONITOR service records. |
5 | * | 3 | * |
6 | * Copyright (C) IBM Corp. 2006 | 4 | * Copyright IBM Corp. 2006, 2009 |
7 | * | 5 | * |
8 | * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com> | 6 | * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com> |
9 | */ | 7 | */ |
@@ -22,6 +20,7 @@ | |||
22 | #include <linux/ctype.h> | 20 | #include <linux/ctype.h> |
23 | #include <linux/poll.h> | 21 | #include <linux/poll.h> |
24 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
23 | #include <linux/platform_device.h> | ||
25 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
26 | #include <asm/ebcdic.h> | 25 | #include <asm/ebcdic.h> |
27 | #include <asm/io.h> | 26 | #include <asm/io.h> |
@@ -40,7 +39,10 @@ struct mon_buf { | |||
40 | char *data; | 39 | char *data; |
41 | }; | 40 | }; |
42 | 41 | ||
42 | static LIST_HEAD(mon_priv_list); | ||
43 | |||
43 | struct mon_private { | 44 | struct mon_private { |
45 | struct list_head priv_list; | ||
44 | struct list_head list; | 46 | struct list_head list; |
45 | struct monwrite_hdr hdr; | 47 | struct monwrite_hdr hdr; |
46 | size_t hdr_to_read; | 48 | size_t hdr_to_read; |
@@ -188,6 +190,7 @@ static int monwrite_open(struct inode *inode, struct file *filp) | |||
188 | monpriv->hdr_to_read = sizeof(monpriv->hdr); | 190 | monpriv->hdr_to_read = sizeof(monpriv->hdr); |
189 | mutex_init(&monpriv->thread_mutex); | 191 | mutex_init(&monpriv->thread_mutex); |
190 | filp->private_data = monpriv; | 192 | filp->private_data = monpriv; |
193 | list_add_tail(&monpriv->priv_list, &mon_priv_list); | ||
191 | unlock_kernel(); | 194 | unlock_kernel(); |
192 | return nonseekable_open(inode, filp); | 195 | return nonseekable_open(inode, filp); |
193 | } | 196 | } |
@@ -206,6 +209,7 @@ static int monwrite_close(struct inode *inode, struct file *filp) | |||
206 | kfree(entry->data); | 209 | kfree(entry->data); |
207 | kfree(entry); | 210 | kfree(entry); |
208 | } | 211 | } |
212 | list_del(&monpriv->priv_list); | ||
209 | kfree(monpriv); | 213 | kfree(monpriv); |
210 | return 0; | 214 | return 0; |
211 | } | 215 | } |
@@ -281,20 +285,102 @@ static struct miscdevice mon_dev = { | |||
281 | }; | 285 | }; |
282 | 286 | ||
283 | /* | 287 | /* |
288 | * suspend/resume | ||
289 | */ | ||
290 | |||
291 | static int monwriter_freeze(struct device *dev) | ||
292 | { | ||
293 | struct mon_private *monpriv; | ||
294 | struct mon_buf *monbuf; | ||
295 | |||
296 | list_for_each_entry(monpriv, &mon_priv_list, priv_list) { | ||
297 | list_for_each_entry(monbuf, &monpriv->list, list) { | ||
298 | if (monbuf->hdr.mon_function != MONWRITE_GEN_EVENT) | ||
299 | monwrite_diag(&monbuf->hdr, monbuf->data, | ||
300 | APPLDATA_STOP_REC); | ||
301 | } | ||
302 | } | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static int monwriter_restore(struct device *dev) | ||
307 | { | ||
308 | struct mon_private *monpriv; | ||
309 | struct mon_buf *monbuf; | ||
310 | |||
311 | list_for_each_entry(monpriv, &mon_priv_list, priv_list) { | ||
312 | list_for_each_entry(monbuf, &monpriv->list, list) { | ||
313 | if (monbuf->hdr.mon_function == MONWRITE_START_INTERVAL) | ||
314 | monwrite_diag(&monbuf->hdr, monbuf->data, | ||
315 | APPLDATA_START_INTERVAL_REC); | ||
316 | if (monbuf->hdr.mon_function == MONWRITE_START_CONFIG) | ||
317 | monwrite_diag(&monbuf->hdr, monbuf->data, | ||
318 | APPLDATA_START_CONFIG_REC); | ||
319 | } | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int monwriter_thaw(struct device *dev) | ||
325 | { | ||
326 | return monwriter_restore(dev); | ||
327 | } | ||
328 | |||
329 | static struct dev_pm_ops monwriter_pm_ops = { | ||
330 | .freeze = monwriter_freeze, | ||
331 | .thaw = monwriter_thaw, | ||
332 | .restore = monwriter_restore, | ||
333 | }; | ||
334 | |||
335 | static struct platform_driver monwriter_pdrv = { | ||
336 | .driver = { | ||
337 | .name = "monwriter", | ||
338 | .owner = THIS_MODULE, | ||
339 | .pm = &monwriter_pm_ops, | ||
340 | }, | ||
341 | }; | ||
342 | |||
343 | static struct platform_device *monwriter_pdev; | ||
344 | |||
345 | /* | ||
284 | * module init/exit | 346 | * module init/exit |
285 | */ | 347 | */ |
286 | 348 | ||
287 | static int __init mon_init(void) | 349 | static int __init mon_init(void) |
288 | { | 350 | { |
289 | if (MACHINE_IS_VM) | 351 | int rc; |
290 | return misc_register(&mon_dev); | 352 | |
291 | else | 353 | if (!MACHINE_IS_VM) |
292 | return -ENODEV; | 354 | return -ENODEV; |
355 | |||
356 | rc = platform_driver_register(&monwriter_pdrv); | ||
357 | if (rc) | ||
358 | return rc; | ||
359 | |||
360 | monwriter_pdev = platform_device_register_simple("monwriter", -1, NULL, | ||
361 | 0); | ||
362 | if (IS_ERR(monwriter_pdev)) { | ||
363 | rc = PTR_ERR(monwriter_pdev); | ||
364 | goto out_driver; | ||
365 | } | ||
366 | |||
367 | rc = misc_register(&mon_dev); | ||
368 | if (rc) | ||
369 | goto out_device; | ||
370 | return 0; | ||
371 | |||
372 | out_device: | ||
373 | platform_device_unregister(monwriter_pdev); | ||
374 | out_driver: | ||
375 | platform_driver_unregister(&monwriter_pdrv); | ||
376 | return rc; | ||
293 | } | 377 | } |
294 | 378 | ||
295 | static void __exit mon_exit(void) | 379 | static void __exit mon_exit(void) |
296 | { | 380 | { |
297 | WARN_ON(misc_deregister(&mon_dev) != 0); | 381 | WARN_ON(misc_deregister(&mon_dev) != 0); |
382 | platform_device_unregister(monwriter_pdev); | ||
383 | platform_driver_unregister(&monwriter_pdrv); | ||
298 | } | 384 | } |
299 | 385 | ||
300 | module_init(mon_init); | 386 | module_init(mon_init); |
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 0b15cf107ec9..81c151b5f0ac 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/raw3270.c | 2 | * IBM/3270 Driver - core functions. |
3 | * IBM/3270 Driver - core functions. | ||
4 | * | 3 | * |
5 | * Author(s): | 4 | * Author(s): |
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | 5 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) |
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | 7 | * Copyright IBM Corp. 2003, 2009 |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/bootmem.h> | 10 | #include <linux/bootmem.h> |
@@ -61,6 +60,7 @@ struct raw3270 { | |||
61 | #define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ | 60 | #define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ |
62 | #define RAW3270_FLAGS_READY 4 /* Device is useable by views */ | 61 | #define RAW3270_FLAGS_READY 4 /* Device is useable by views */ |
63 | #define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ | 62 | #define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ |
63 | #define RAW3270_FLAGS_FROZEN 16 /* set if 3270 is frozen for suspend */ | ||
64 | 64 | ||
65 | /* Semaphore to protect global data of raw3270 (devices, views, etc). */ | 65 | /* Semaphore to protect global data of raw3270 (devices, views, etc). */ |
66 | static DEFINE_MUTEX(raw3270_mutex); | 66 | static DEFINE_MUTEX(raw3270_mutex); |
@@ -306,7 +306,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) | |||
306 | 306 | ||
307 | spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); | 307 | spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); |
308 | rp = view->dev; | 308 | rp = view->dev; |
309 | if (!rp || rp->view != view) | 309 | if (!rp || rp->view != view || |
310 | test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) | ||
310 | rc = -EACCES; | 311 | rc = -EACCES; |
311 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) | 312 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) |
312 | rc = -ENODEV; | 313 | rc = -ENODEV; |
@@ -323,7 +324,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) | |||
323 | int rc; | 324 | int rc; |
324 | 325 | ||
325 | rp = view->dev; | 326 | rp = view->dev; |
326 | if (!rp || rp->view != view) | 327 | if (!rp || rp->view != view || |
328 | test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) | ||
327 | rc = -EACCES; | 329 | rc = -EACCES; |
328 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) | 330 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) |
329 | rc = -ENODEV; | 331 | rc = -ENODEV; |
@@ -764,7 +766,8 @@ raw3270_reset(struct raw3270_view *view) | |||
764 | int rc; | 766 | int rc; |
765 | 767 | ||
766 | rp = view->dev; | 768 | rp = view->dev; |
767 | if (!rp || rp->view != view) | 769 | if (!rp || rp->view != view || |
770 | test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) | ||
768 | rc = -EACCES; | 771 | rc = -EACCES; |
769 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) | 772 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) |
770 | rc = -ENODEV; | 773 | rc = -ENODEV; |
@@ -922,6 +925,8 @@ raw3270_activate_view(struct raw3270_view *view) | |||
922 | rc = 0; | 925 | rc = 0; |
923 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) | 926 | else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) |
924 | rc = -ENODEV; | 927 | rc = -ENODEV; |
928 | else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) | ||
929 | rc = -EACCES; | ||
925 | else { | 930 | else { |
926 | oldview = NULL; | 931 | oldview = NULL; |
927 | if (rp->view) { | 932 | if (rp->view) { |
@@ -969,7 +974,8 @@ raw3270_deactivate_view(struct raw3270_view *view) | |||
969 | list_del_init(&view->list); | 974 | list_del_init(&view->list); |
970 | list_add_tail(&view->list, &rp->view_list); | 975 | list_add_tail(&view->list, &rp->view_list); |
971 | /* Try to activate another view. */ | 976 | /* Try to activate another view. */ |
972 | if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { | 977 | if (test_bit(RAW3270_FLAGS_READY, &rp->flags) && |
978 | !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { | ||
973 | list_for_each_entry(view, &rp->view_list, list) { | 979 | list_for_each_entry(view, &rp->view_list, list) { |
974 | rp->view = view; | 980 | rp->view = view; |
975 | if (view->fn->activate(view) == 0) | 981 | if (view->fn->activate(view) == 0) |
@@ -1068,7 +1074,8 @@ raw3270_del_view(struct raw3270_view *view) | |||
1068 | rp->view = NULL; | 1074 | rp->view = NULL; |
1069 | } | 1075 | } |
1070 | list_del_init(&view->list); | 1076 | list_del_init(&view->list); |
1071 | if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags)) { | 1077 | if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) && |
1078 | !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { | ||
1072 | /* Try to activate another view. */ | 1079 | /* Try to activate another view. */ |
1073 | list_for_each_entry(nv, &rp->view_list, list) { | 1080 | list_for_each_entry(nv, &rp->view_list, list) { |
1074 | if (nv->fn->activate(nv) == 0) { | 1081 | if (nv->fn->activate(nv) == 0) { |
@@ -1337,6 +1344,58 @@ raw3270_set_offline (struct ccw_device *cdev) | |||
1337 | return 0; | 1344 | return 0; |
1338 | } | 1345 | } |
1339 | 1346 | ||
1347 | static int raw3270_pm_stop(struct ccw_device *cdev) | ||
1348 | { | ||
1349 | struct raw3270 *rp; | ||
1350 | struct raw3270_view *view; | ||
1351 | unsigned long flags; | ||
1352 | |||
1353 | rp = cdev->dev.driver_data; | ||
1354 | if (!rp) | ||
1355 | return 0; | ||
1356 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
1357 | if (rp->view) | ||
1358 | rp->view->fn->deactivate(rp->view); | ||
1359 | if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) { | ||
1360 | /* | ||
1361 | * Release tty and fullscreen for all non-console | ||
1362 | * devices. | ||
1363 | */ | ||
1364 | list_for_each_entry(view, &rp->view_list, list) { | ||
1365 | if (view->fn->release) | ||
1366 | view->fn->release(view); | ||
1367 | } | ||
1368 | } | ||
1369 | set_bit(RAW3270_FLAGS_FROZEN, &rp->flags); | ||
1370 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | static int raw3270_pm_start(struct ccw_device *cdev) | ||
1375 | { | ||
1376 | struct raw3270 *rp; | ||
1377 | unsigned long flags; | ||
1378 | |||
1379 | rp = cdev->dev.driver_data; | ||
1380 | if (!rp) | ||
1381 | return 0; | ||
1382 | spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); | ||
1383 | clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags); | ||
1384 | if (rp->view) | ||
1385 | rp->view->fn->activate(rp->view); | ||
1386 | spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); | ||
1387 | return 0; | ||
1388 | } | ||
1389 | |||
1390 | void raw3270_pm_unfreeze(struct raw3270_view *view) | ||
1391 | { | ||
1392 | struct raw3270 *rp; | ||
1393 | |||
1394 | rp = view->dev; | ||
1395 | if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) | ||
1396 | ccw_device_force_console(); | ||
1397 | } | ||
1398 | |||
1340 | static struct ccw_device_id raw3270_id[] = { | 1399 | static struct ccw_device_id raw3270_id[] = { |
1341 | { CCW_DEVICE(0x3270, 0) }, | 1400 | { CCW_DEVICE(0x3270, 0) }, |
1342 | { CCW_DEVICE(0x3271, 0) }, | 1401 | { CCW_DEVICE(0x3271, 0) }, |
@@ -1360,6 +1419,9 @@ static struct ccw_driver raw3270_ccw_driver = { | |||
1360 | .remove = &raw3270_remove, | 1419 | .remove = &raw3270_remove, |
1361 | .set_online = &raw3270_set_online, | 1420 | .set_online = &raw3270_set_online, |
1362 | .set_offline = &raw3270_set_offline, | 1421 | .set_offline = &raw3270_set_offline, |
1422 | .freeze = &raw3270_pm_stop, | ||
1423 | .thaw = &raw3270_pm_start, | ||
1424 | .restore = &raw3270_pm_start, | ||
1363 | }; | 1425 | }; |
1364 | 1426 | ||
1365 | static int | 1427 | static int |
diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 90beaa80a782..ed34eb2199cc 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/raw3270.h | 2 | * IBM/3270 Driver |
3 | * IBM/3270 Driver | ||
4 | * | 3 | * |
5 | * Author(s): | 4 | * Author(s): |
6 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) | 5 | * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) |
7 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * -- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | 7 | * Copyright IBM Corp. 2003, 2009 |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <asm/idals.h> | 10 | #include <asm/idals.h> |
@@ -195,6 +194,7 @@ void raw3270_wait_cons_dev(struct raw3270 *); | |||
195 | /* Notifier for device addition/removal */ | 194 | /* Notifier for device addition/removal */ |
196 | int raw3270_register_notifier(void (*notifier)(int, int)); | 195 | int raw3270_register_notifier(void (*notifier)(int, int)); |
197 | void raw3270_unregister_notifier(void (*notifier)(int, int)); | 196 | void raw3270_unregister_notifier(void (*notifier)(int, int)); |
197 | void raw3270_pm_unfreeze(struct raw3270_view *); | ||
198 | 198 | ||
199 | /* | 199 | /* |
200 | * Little memory allocator for string objects. | 200 | * Little memory allocator for string objects. |
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 4377e93a43d7..a983f5086788 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp.c | 2 | * core function to access sclp interface |
3 | * core function to access sclp interface | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corp. 1999, 2009 |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/module.h> | 10 | #include <linux/module.h> |
@@ -16,6 +15,9 @@ | |||
16 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
17 | #include <linux/jiffies.h> | 16 | #include <linux/jiffies.h> |
18 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/suspend.h> | ||
19 | #include <linux/completion.h> | ||
20 | #include <linux/platform_device.h> | ||
19 | #include <asm/types.h> | 21 | #include <asm/types.h> |
20 | #include <asm/s390_ext.h> | 22 | #include <asm/s390_ext.h> |
21 | 23 | ||
@@ -47,6 +49,16 @@ static struct sclp_req sclp_init_req; | |||
47 | static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); | 49 | static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); |
48 | static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); | 50 | static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); |
49 | 51 | ||
52 | /* Suspend request */ | ||
53 | static DECLARE_COMPLETION(sclp_request_queue_flushed); | ||
54 | |||
55 | static void sclp_suspend_req_cb(struct sclp_req *req, void *data) | ||
56 | { | ||
57 | complete(&sclp_request_queue_flushed); | ||
58 | } | ||
59 | |||
60 | static struct sclp_req sclp_suspend_req; | ||
61 | |||
50 | /* Timer for request retries. */ | 62 | /* Timer for request retries. */ |
51 | static struct timer_list sclp_request_timer; | 63 | static struct timer_list sclp_request_timer; |
52 | 64 | ||
@@ -84,6 +96,12 @@ static volatile enum sclp_mask_state_t { | |||
84 | sclp_mask_state_initializing | 96 | sclp_mask_state_initializing |
85 | } sclp_mask_state = sclp_mask_state_idle; | 97 | } sclp_mask_state = sclp_mask_state_idle; |
86 | 98 | ||
99 | /* Internal state: is the driver suspended? */ | ||
100 | static enum sclp_suspend_state_t { | ||
101 | sclp_suspend_state_running, | ||
102 | sclp_suspend_state_suspended, | ||
103 | } sclp_suspend_state = sclp_suspend_state_running; | ||
104 | |||
87 | /* Maximum retry counts */ | 105 | /* Maximum retry counts */ |
88 | #define SCLP_INIT_RETRY 3 | 106 | #define SCLP_INIT_RETRY 3 |
89 | #define SCLP_MASK_RETRY 3 | 107 | #define SCLP_MASK_RETRY 3 |
@@ -211,6 +229,8 @@ sclp_process_queue(void) | |||
211 | del_timer(&sclp_request_timer); | 229 | del_timer(&sclp_request_timer); |
212 | while (!list_empty(&sclp_req_queue)) { | 230 | while (!list_empty(&sclp_req_queue)) { |
213 | req = list_entry(sclp_req_queue.next, struct sclp_req, list); | 231 | req = list_entry(sclp_req_queue.next, struct sclp_req, list); |
232 | if (!req->sccb) | ||
233 | goto do_post; | ||
214 | rc = __sclp_start_request(req); | 234 | rc = __sclp_start_request(req); |
215 | if (rc == 0) | 235 | if (rc == 0) |
216 | break; | 236 | break; |
@@ -222,6 +242,7 @@ sclp_process_queue(void) | |||
222 | sclp_request_timeout, 0); | 242 | sclp_request_timeout, 0); |
223 | break; | 243 | break; |
224 | } | 244 | } |
245 | do_post: | ||
225 | /* Post-processing for aborted request */ | 246 | /* Post-processing for aborted request */ |
226 | list_del(&req->list); | 247 | list_del(&req->list); |
227 | if (req->callback) { | 248 | if (req->callback) { |
@@ -233,6 +254,19 @@ sclp_process_queue(void) | |||
233 | spin_unlock_irqrestore(&sclp_lock, flags); | 254 | spin_unlock_irqrestore(&sclp_lock, flags); |
234 | } | 255 | } |
235 | 256 | ||
257 | static int __sclp_can_add_request(struct sclp_req *req) | ||
258 | { | ||
259 | if (req == &sclp_suspend_req || req == &sclp_init_req) | ||
260 | return 1; | ||
261 | if (sclp_suspend_state != sclp_suspend_state_running) | ||
262 | return 0; | ||
263 | if (sclp_init_state != sclp_init_state_initialized) | ||
264 | return 0; | ||
265 | if (sclp_activation_state != sclp_activation_state_active) | ||
266 | return 0; | ||
267 | return 1; | ||
268 | } | ||
269 | |||
236 | /* Queue a new request. Return zero on success, non-zero otherwise. */ | 270 | /* Queue a new request. Return zero on success, non-zero otherwise. */ |
237 | int | 271 | int |
238 | sclp_add_request(struct sclp_req *req) | 272 | sclp_add_request(struct sclp_req *req) |
@@ -241,9 +275,7 @@ sclp_add_request(struct sclp_req *req) | |||
241 | int rc; | 275 | int rc; |
242 | 276 | ||
243 | spin_lock_irqsave(&sclp_lock, flags); | 277 | spin_lock_irqsave(&sclp_lock, flags); |
244 | if ((sclp_init_state != sclp_init_state_initialized || | 278 | if (!__sclp_can_add_request(req)) { |
245 | sclp_activation_state != sclp_activation_state_active) && | ||
246 | req != &sclp_init_req) { | ||
247 | spin_unlock_irqrestore(&sclp_lock, flags); | 279 | spin_unlock_irqrestore(&sclp_lock, flags); |
248 | return -EIO; | 280 | return -EIO; |
249 | } | 281 | } |
@@ -254,10 +286,16 @@ sclp_add_request(struct sclp_req *req) | |||
254 | /* Start if request is first in list */ | 286 | /* Start if request is first in list */ |
255 | if (sclp_running_state == sclp_running_state_idle && | 287 | if (sclp_running_state == sclp_running_state_idle && |
256 | req->list.prev == &sclp_req_queue) { | 288 | req->list.prev == &sclp_req_queue) { |
289 | if (!req->sccb) { | ||
290 | list_del(&req->list); | ||
291 | rc = -ENODATA; | ||
292 | goto out; | ||
293 | } | ||
257 | rc = __sclp_start_request(req); | 294 | rc = __sclp_start_request(req); |
258 | if (rc) | 295 | if (rc) |
259 | list_del(&req->list); | 296 | list_del(&req->list); |
260 | } | 297 | } |
298 | out: | ||
261 | spin_unlock_irqrestore(&sclp_lock, flags); | 299 | spin_unlock_irqrestore(&sclp_lock, flags); |
262 | return rc; | 300 | return rc; |
263 | } | 301 | } |
@@ -560,6 +598,7 @@ sclp_register(struct sclp_register *reg) | |||
560 | /* Trigger initial state change callback */ | 598 | /* Trigger initial state change callback */ |
561 | reg->sclp_receive_mask = 0; | 599 | reg->sclp_receive_mask = 0; |
562 | reg->sclp_send_mask = 0; | 600 | reg->sclp_send_mask = 0; |
601 | reg->pm_event_posted = 0; | ||
563 | list_add(®->list, &sclp_reg_list); | 602 | list_add(®->list, &sclp_reg_list); |
564 | spin_unlock_irqrestore(&sclp_lock, flags); | 603 | spin_unlock_irqrestore(&sclp_lock, flags); |
565 | rc = sclp_init_mask(1); | 604 | rc = sclp_init_mask(1); |
@@ -880,20 +919,134 @@ static struct notifier_block sclp_reboot_notifier = { | |||
880 | .notifier_call = sclp_reboot_event | 919 | .notifier_call = sclp_reboot_event |
881 | }; | 920 | }; |
882 | 921 | ||
922 | /* | ||
923 | * Suspend/resume SCLP notifier implementation | ||
924 | */ | ||
925 | |||
926 | static void sclp_pm_event(enum sclp_pm_event sclp_pm_event, int rollback) | ||
927 | { | ||
928 | struct sclp_register *reg; | ||
929 | unsigned long flags; | ||
930 | |||
931 | if (!rollback) { | ||
932 | spin_lock_irqsave(&sclp_lock, flags); | ||
933 | list_for_each_entry(reg, &sclp_reg_list, list) | ||
934 | reg->pm_event_posted = 0; | ||
935 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
936 | } | ||
937 | do { | ||
938 | spin_lock_irqsave(&sclp_lock, flags); | ||
939 | list_for_each_entry(reg, &sclp_reg_list, list) { | ||
940 | if (rollback && reg->pm_event_posted) | ||
941 | goto found; | ||
942 | if (!rollback && !reg->pm_event_posted) | ||
943 | goto found; | ||
944 | } | ||
945 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
946 | return; | ||
947 | found: | ||
948 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
949 | if (reg->pm_event_fn) | ||
950 | reg->pm_event_fn(reg, sclp_pm_event); | ||
951 | reg->pm_event_posted = rollback ? 0 : 1; | ||
952 | } while (1); | ||
953 | } | ||
954 | |||
955 | /* | ||
956 | * Susend/resume callbacks for platform device | ||
957 | */ | ||
958 | |||
959 | static int sclp_freeze(struct device *dev) | ||
960 | { | ||
961 | unsigned long flags; | ||
962 | int rc; | ||
963 | |||
964 | sclp_pm_event(SCLP_PM_EVENT_FREEZE, 0); | ||
965 | |||
966 | spin_lock_irqsave(&sclp_lock, flags); | ||
967 | sclp_suspend_state = sclp_suspend_state_suspended; | ||
968 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
969 | |||
970 | /* Init supend data */ | ||
971 | memset(&sclp_suspend_req, 0, sizeof(sclp_suspend_req)); | ||
972 | sclp_suspend_req.callback = sclp_suspend_req_cb; | ||
973 | sclp_suspend_req.status = SCLP_REQ_FILLED; | ||
974 | init_completion(&sclp_request_queue_flushed); | ||
975 | |||
976 | rc = sclp_add_request(&sclp_suspend_req); | ||
977 | if (rc == 0) | ||
978 | wait_for_completion(&sclp_request_queue_flushed); | ||
979 | else if (rc != -ENODATA) | ||
980 | goto fail_thaw; | ||
981 | |||
982 | rc = sclp_deactivate(); | ||
983 | if (rc) | ||
984 | goto fail_thaw; | ||
985 | return 0; | ||
986 | |||
987 | fail_thaw: | ||
988 | spin_lock_irqsave(&sclp_lock, flags); | ||
989 | sclp_suspend_state = sclp_suspend_state_running; | ||
990 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
991 | sclp_pm_event(SCLP_PM_EVENT_THAW, 1); | ||
992 | return rc; | ||
993 | } | ||
994 | |||
995 | static int sclp_undo_suspend(enum sclp_pm_event event) | ||
996 | { | ||
997 | unsigned long flags; | ||
998 | int rc; | ||
999 | |||
1000 | rc = sclp_reactivate(); | ||
1001 | if (rc) | ||
1002 | return rc; | ||
1003 | |||
1004 | spin_lock_irqsave(&sclp_lock, flags); | ||
1005 | sclp_suspend_state = sclp_suspend_state_running; | ||
1006 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
1007 | |||
1008 | sclp_pm_event(event, 0); | ||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | static int sclp_thaw(struct device *dev) | ||
1013 | { | ||
1014 | return sclp_undo_suspend(SCLP_PM_EVENT_THAW); | ||
1015 | } | ||
1016 | |||
1017 | static int sclp_restore(struct device *dev) | ||
1018 | { | ||
1019 | return sclp_undo_suspend(SCLP_PM_EVENT_RESTORE); | ||
1020 | } | ||
1021 | |||
1022 | static struct dev_pm_ops sclp_pm_ops = { | ||
1023 | .freeze = sclp_freeze, | ||
1024 | .thaw = sclp_thaw, | ||
1025 | .restore = sclp_restore, | ||
1026 | }; | ||
1027 | |||
1028 | static struct platform_driver sclp_pdrv = { | ||
1029 | .driver = { | ||
1030 | .name = "sclp", | ||
1031 | .owner = THIS_MODULE, | ||
1032 | .pm = &sclp_pm_ops, | ||
1033 | }, | ||
1034 | }; | ||
1035 | |||
1036 | static struct platform_device *sclp_pdev; | ||
1037 | |||
883 | /* Initialize SCLP driver. Return zero if driver is operational, non-zero | 1038 | /* Initialize SCLP driver. Return zero if driver is operational, non-zero |
884 | * otherwise. */ | 1039 | * otherwise. */ |
885 | static int | 1040 | static int |
886 | sclp_init(void) | 1041 | sclp_init(void) |
887 | { | 1042 | { |
888 | unsigned long flags; | 1043 | unsigned long flags; |
889 | int rc; | 1044 | int rc = 0; |
890 | 1045 | ||
891 | spin_lock_irqsave(&sclp_lock, flags); | 1046 | spin_lock_irqsave(&sclp_lock, flags); |
892 | /* Check for previous or running initialization */ | 1047 | /* Check for previous or running initialization */ |
893 | if (sclp_init_state != sclp_init_state_uninitialized) { | 1048 | if (sclp_init_state != sclp_init_state_uninitialized) |
894 | spin_unlock_irqrestore(&sclp_lock, flags); | 1049 | goto fail_unlock; |
895 | return 0; | ||
896 | } | ||
897 | sclp_init_state = sclp_init_state_initializing; | 1050 | sclp_init_state = sclp_init_state_initializing; |
898 | /* Set up variables */ | 1051 | /* Set up variables */ |
899 | INIT_LIST_HEAD(&sclp_req_queue); | 1052 | INIT_LIST_HEAD(&sclp_req_queue); |
@@ -904,27 +1057,17 @@ sclp_init(void) | |||
904 | spin_unlock_irqrestore(&sclp_lock, flags); | 1057 | spin_unlock_irqrestore(&sclp_lock, flags); |
905 | rc = sclp_check_interface(); | 1058 | rc = sclp_check_interface(); |
906 | spin_lock_irqsave(&sclp_lock, flags); | 1059 | spin_lock_irqsave(&sclp_lock, flags); |
907 | if (rc) { | 1060 | if (rc) |
908 | sclp_init_state = sclp_init_state_uninitialized; | 1061 | goto fail_init_state_uninitialized; |
909 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
910 | return rc; | ||
911 | } | ||
912 | /* Register reboot handler */ | 1062 | /* Register reboot handler */ |
913 | rc = register_reboot_notifier(&sclp_reboot_notifier); | 1063 | rc = register_reboot_notifier(&sclp_reboot_notifier); |
914 | if (rc) { | 1064 | if (rc) |
915 | sclp_init_state = sclp_init_state_uninitialized; | 1065 | goto fail_init_state_uninitialized; |
916 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
917 | return rc; | ||
918 | } | ||
919 | /* Register interrupt handler */ | 1066 | /* Register interrupt handler */ |
920 | rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler, | 1067 | rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler, |
921 | &ext_int_info_hwc); | 1068 | &ext_int_info_hwc); |
922 | if (rc) { | 1069 | if (rc) |
923 | unregister_reboot_notifier(&sclp_reboot_notifier); | 1070 | goto fail_unregister_reboot_notifier; |
924 | sclp_init_state = sclp_init_state_uninitialized; | ||
925 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
926 | return rc; | ||
927 | } | ||
928 | sclp_init_state = sclp_init_state_initialized; | 1071 | sclp_init_state = sclp_init_state_initialized; |
929 | spin_unlock_irqrestore(&sclp_lock, flags); | 1072 | spin_unlock_irqrestore(&sclp_lock, flags); |
930 | /* Enable service-signal external interruption - needs to happen with | 1073 | /* Enable service-signal external interruption - needs to happen with |
@@ -932,11 +1075,56 @@ sclp_init(void) | |||
932 | ctl_set_bit(0, 9); | 1075 | ctl_set_bit(0, 9); |
933 | sclp_init_mask(1); | 1076 | sclp_init_mask(1); |
934 | return 0; | 1077 | return 0; |
1078 | |||
1079 | fail_unregister_reboot_notifier: | ||
1080 | unregister_reboot_notifier(&sclp_reboot_notifier); | ||
1081 | fail_init_state_uninitialized: | ||
1082 | sclp_init_state = sclp_init_state_uninitialized; | ||
1083 | fail_unlock: | ||
1084 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
1085 | return rc; | ||
935 | } | 1086 | } |
936 | 1087 | ||
1088 | /* | ||
1089 | * SCLP panic notifier: If we are suspended, we thaw SCLP in order to be able | ||
1090 | * to print the panic message. | ||
1091 | */ | ||
1092 | static int sclp_panic_notify(struct notifier_block *self, | ||
1093 | unsigned long event, void *data) | ||
1094 | { | ||
1095 | if (sclp_suspend_state == sclp_suspend_state_suspended) | ||
1096 | sclp_undo_suspend(SCLP_PM_EVENT_THAW); | ||
1097 | return NOTIFY_OK; | ||
1098 | } | ||
1099 | |||
1100 | static struct notifier_block sclp_on_panic_nb = { | ||
1101 | .notifier_call = sclp_panic_notify, | ||
1102 | .priority = SCLP_PANIC_PRIO, | ||
1103 | }; | ||
1104 | |||
937 | static __init int sclp_initcall(void) | 1105 | static __init int sclp_initcall(void) |
938 | { | 1106 | { |
1107 | int rc; | ||
1108 | |||
1109 | rc = platform_driver_register(&sclp_pdrv); | ||
1110 | if (rc) | ||
1111 | return rc; | ||
1112 | sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); | ||
1113 | rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; | ||
1114 | if (rc) | ||
1115 | goto fail_platform_driver_unregister; | ||
1116 | rc = atomic_notifier_chain_register(&panic_notifier_list, | ||
1117 | &sclp_on_panic_nb); | ||
1118 | if (rc) | ||
1119 | goto fail_platform_device_unregister; | ||
1120 | |||
939 | return sclp_init(); | 1121 | return sclp_init(); |
1122 | |||
1123 | fail_platform_device_unregister: | ||
1124 | platform_device_unregister(sclp_pdev); | ||
1125 | fail_platform_driver_unregister: | ||
1126 | platform_driver_unregister(&sclp_pdrv); | ||
1127 | return rc; | ||
940 | } | 1128 | } |
941 | 1129 | ||
942 | arch_initcall(sclp_initcall); | 1130 | arch_initcall(sclp_initcall); |
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index bac80e856f97..60e7cb07095b 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h | |||
@@ -1,10 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp.h | 2 | * Copyright IBM Corp. 1999, 2009 |
3 | * | 3 | * |
4 | * S390 version | 4 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
5 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | ||
7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
8 | */ | 6 | */ |
9 | 7 | ||
10 | #ifndef __SCLP_H__ | 8 | #ifndef __SCLP_H__ |
@@ -17,7 +15,7 @@ | |||
17 | 15 | ||
18 | /* maximum number of pages concerning our own memory management */ | 16 | /* maximum number of pages concerning our own memory management */ |
19 | #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) | 17 | #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) |
20 | #define MAX_CONSOLE_PAGES 4 | 18 | #define MAX_CONSOLE_PAGES 6 |
21 | 19 | ||
22 | #define EVTYP_OPCMD 0x01 | 20 | #define EVTYP_OPCMD 0x01 |
23 | #define EVTYP_MSG 0x02 | 21 | #define EVTYP_MSG 0x02 |
@@ -68,6 +66,15 @@ typedef unsigned int sclp_cmdw_t; | |||
68 | 66 | ||
69 | #define GDS_KEY_SELFDEFTEXTMSG 0x31 | 67 | #define GDS_KEY_SELFDEFTEXTMSG 0x31 |
70 | 68 | ||
69 | enum sclp_pm_event { | ||
70 | SCLP_PM_EVENT_FREEZE, | ||
71 | SCLP_PM_EVENT_THAW, | ||
72 | SCLP_PM_EVENT_RESTORE, | ||
73 | }; | ||
74 | |||
75 | #define SCLP_PANIC_PRIO 1 | ||
76 | #define SCLP_PANIC_PRIO_CLIENT 0 | ||
77 | |||
71 | typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ | 78 | typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ |
72 | 79 | ||
73 | struct sccb_header { | 80 | struct sccb_header { |
@@ -134,6 +141,10 @@ struct sclp_register { | |||
134 | void (*state_change_fn)(struct sclp_register *); | 141 | void (*state_change_fn)(struct sclp_register *); |
135 | /* called for events in cp_receive_mask/sclp_receive_mask */ | 142 | /* called for events in cp_receive_mask/sclp_receive_mask */ |
136 | void (*receiver_fn)(struct evbuf_header *); | 143 | void (*receiver_fn)(struct evbuf_header *); |
144 | /* called for power management events */ | ||
145 | void (*pm_event_fn)(struct sclp_register *, enum sclp_pm_event); | ||
146 | /* pm event posted flag */ | ||
147 | int pm_event_posted; | ||
137 | }; | 148 | }; |
138 | 149 | ||
139 | /* externals from sclp.c */ | 150 | /* externals from sclp.c */ |
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 77ab6e34a100..5cc11c636d38 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c | |||
@@ -1,9 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_cmd.c | 2 | * Copyright IBM Corp. 2007, 2009 |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2007 | 4 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, |
5 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, | 5 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
6 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | ||
7 | */ | 6 | */ |
8 | 7 | ||
9 | #define KMSG_COMPONENT "sclp_cmd" | 8 | #define KMSG_COMPONENT "sclp_cmd" |
@@ -12,11 +11,13 @@ | |||
12 | #include <linux/completion.h> | 11 | #include <linux/completion.h> |
13 | #include <linux/init.h> | 12 | #include <linux/init.h> |
14 | #include <linux/errno.h> | 13 | #include <linux/errno.h> |
14 | #include <linux/err.h> | ||
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/string.h> | 16 | #include <linux/string.h> |
17 | #include <linux/mm.h> | 17 | #include <linux/mm.h> |
18 | #include <linux/mmzone.h> | 18 | #include <linux/mmzone.h> |
19 | #include <linux/memory.h> | 19 | #include <linux/memory.h> |
20 | #include <linux/platform_device.h> | ||
20 | #include <asm/chpid.h> | 21 | #include <asm/chpid.h> |
21 | #include <asm/sclp.h> | 22 | #include <asm/sclp.h> |
22 | #include <asm/setup.h> | 23 | #include <asm/setup.h> |
@@ -292,6 +293,7 @@ static DEFINE_MUTEX(sclp_mem_mutex); | |||
292 | static LIST_HEAD(sclp_mem_list); | 293 | static LIST_HEAD(sclp_mem_list); |
293 | static u8 sclp_max_storage_id; | 294 | static u8 sclp_max_storage_id; |
294 | static unsigned long sclp_storage_ids[256 / BITS_PER_LONG]; | 295 | static unsigned long sclp_storage_ids[256 / BITS_PER_LONG]; |
296 | static int sclp_mem_state_changed; | ||
295 | 297 | ||
296 | struct memory_increment { | 298 | struct memory_increment { |
297 | struct list_head list; | 299 | struct list_head list; |
@@ -450,6 +452,8 @@ static int sclp_mem_notifier(struct notifier_block *nb, | |||
450 | rc = -EINVAL; | 452 | rc = -EINVAL; |
451 | break; | 453 | break; |
452 | } | 454 | } |
455 | if (!rc) | ||
456 | sclp_mem_state_changed = 1; | ||
453 | mutex_unlock(&sclp_mem_mutex); | 457 | mutex_unlock(&sclp_mem_mutex); |
454 | return rc ? NOTIFY_BAD : NOTIFY_OK; | 458 | return rc ? NOTIFY_BAD : NOTIFY_OK; |
455 | } | 459 | } |
@@ -525,6 +529,14 @@ static void __init insert_increment(u16 rn, int standby, int assigned) | |||
525 | list_add(&new_incr->list, prev); | 529 | list_add(&new_incr->list, prev); |
526 | } | 530 | } |
527 | 531 | ||
532 | static int sclp_mem_freeze(struct device *dev) | ||
533 | { | ||
534 | if (!sclp_mem_state_changed) | ||
535 | return 0; | ||
536 | pr_err("Memory hotplug state changed, suspend refused.\n"); | ||
537 | return -EPERM; | ||
538 | } | ||
539 | |||
528 | struct read_storage_sccb { | 540 | struct read_storage_sccb { |
529 | struct sccb_header header; | 541 | struct sccb_header header; |
530 | u16 max_id; | 542 | u16 max_id; |
@@ -534,8 +546,20 @@ struct read_storage_sccb { | |||
534 | u32 entries[0]; | 546 | u32 entries[0]; |
535 | } __packed; | 547 | } __packed; |
536 | 548 | ||
549 | static struct dev_pm_ops sclp_mem_pm_ops = { | ||
550 | .freeze = sclp_mem_freeze, | ||
551 | }; | ||
552 | |||
553 | static struct platform_driver sclp_mem_pdrv = { | ||
554 | .driver = { | ||
555 | .name = "sclp_mem", | ||
556 | .pm = &sclp_mem_pm_ops, | ||
557 | }, | ||
558 | }; | ||
559 | |||
537 | static int __init sclp_detect_standby_memory(void) | 560 | static int __init sclp_detect_standby_memory(void) |
538 | { | 561 | { |
562 | struct platform_device *sclp_pdev; | ||
539 | struct read_storage_sccb *sccb; | 563 | struct read_storage_sccb *sccb; |
540 | int i, id, assigned, rc; | 564 | int i, id, assigned, rc; |
541 | 565 | ||
@@ -588,7 +612,17 @@ static int __init sclp_detect_standby_memory(void) | |||
588 | rc = register_memory_notifier(&sclp_mem_nb); | 612 | rc = register_memory_notifier(&sclp_mem_nb); |
589 | if (rc) | 613 | if (rc) |
590 | goto out; | 614 | goto out; |
615 | rc = platform_driver_register(&sclp_mem_pdrv); | ||
616 | if (rc) | ||
617 | goto out; | ||
618 | sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0); | ||
619 | rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; | ||
620 | if (rc) | ||
621 | goto out_driver; | ||
591 | sclp_add_standby_memory(); | 622 | sclp_add_standby_memory(); |
623 | goto out; | ||
624 | out_driver: | ||
625 | platform_driver_unregister(&sclp_mem_pdrv); | ||
592 | out: | 626 | out: |
593 | free_page((unsigned long) sccb); | 627 | free_page((unsigned long) sccb); |
594 | return rc; | 628 | return rc; |
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 9a25c4bd1421..336811a77672 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c | |||
@@ -1,11 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_con.c | 2 | * SCLP line mode console driver |
3 | * SCLP line mode console driver | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corp. 1999, 2009 |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
9 | */ | 7 | */ |
10 | 8 | ||
11 | #include <linux/kmod.h> | 9 | #include <linux/kmod.h> |
@@ -32,13 +30,14 @@ static spinlock_t sclp_con_lock; | |||
32 | static struct list_head sclp_con_pages; | 30 | static struct list_head sclp_con_pages; |
33 | /* List of full struct sclp_buffer structures ready for output */ | 31 | /* List of full struct sclp_buffer structures ready for output */ |
34 | static struct list_head sclp_con_outqueue; | 32 | static struct list_head sclp_con_outqueue; |
35 | /* Counter how many buffers are emitted (max 1) and how many */ | ||
36 | /* are on the output queue. */ | ||
37 | static int sclp_con_buffer_count; | ||
38 | /* Pointer to current console buffer */ | 33 | /* Pointer to current console buffer */ |
39 | static struct sclp_buffer *sclp_conbuf; | 34 | static struct sclp_buffer *sclp_conbuf; |
40 | /* Timer for delayed output of console messages */ | 35 | /* Timer for delayed output of console messages */ |
41 | static struct timer_list sclp_con_timer; | 36 | static struct timer_list sclp_con_timer; |
37 | /* Suspend mode flag */ | ||
38 | static int sclp_con_suspended; | ||
39 | /* Flag that output queue is currently running */ | ||
40 | static int sclp_con_queue_running; | ||
42 | 41 | ||
43 | /* Output format for console messages */ | 42 | /* Output format for console messages */ |
44 | static unsigned short sclp_con_columns; | 43 | static unsigned short sclp_con_columns; |
@@ -53,42 +52,71 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) | |||
53 | do { | 52 | do { |
54 | page = sclp_unmake_buffer(buffer); | 53 | page = sclp_unmake_buffer(buffer); |
55 | spin_lock_irqsave(&sclp_con_lock, flags); | 54 | spin_lock_irqsave(&sclp_con_lock, flags); |
55 | |||
56 | /* Remove buffer from outqueue */ | 56 | /* Remove buffer from outqueue */ |
57 | list_del(&buffer->list); | 57 | list_del(&buffer->list); |
58 | sclp_con_buffer_count--; | ||
59 | list_add_tail((struct list_head *) page, &sclp_con_pages); | 58 | list_add_tail((struct list_head *) page, &sclp_con_pages); |
59 | |||
60 | /* Check if there is a pending buffer on the out queue. */ | 60 | /* Check if there is a pending buffer on the out queue. */ |
61 | buffer = NULL; | 61 | buffer = NULL; |
62 | if (!list_empty(&sclp_con_outqueue)) | 62 | if (!list_empty(&sclp_con_outqueue)) |
63 | buffer = list_entry(sclp_con_outqueue.next, | 63 | buffer = list_first_entry(&sclp_con_outqueue, |
64 | struct sclp_buffer, list); | 64 | struct sclp_buffer, list); |
65 | if (!buffer || sclp_con_suspended) { | ||
66 | sclp_con_queue_running = 0; | ||
67 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
68 | break; | ||
69 | } | ||
65 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 70 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
66 | } while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback)); | 71 | } while (sclp_emit_buffer(buffer, sclp_conbuf_callback)); |
67 | } | 72 | } |
68 | 73 | ||
69 | static void | 74 | /* |
70 | sclp_conbuf_emit(void) | 75 | * Finalize and emit first pending buffer. |
76 | */ | ||
77 | static void sclp_conbuf_emit(void) | ||
71 | { | 78 | { |
72 | struct sclp_buffer* buffer; | 79 | struct sclp_buffer* buffer; |
73 | unsigned long flags; | 80 | unsigned long flags; |
74 | int count; | ||
75 | int rc; | 81 | int rc; |
76 | 82 | ||
77 | spin_lock_irqsave(&sclp_con_lock, flags); | 83 | spin_lock_irqsave(&sclp_con_lock, flags); |
78 | buffer = sclp_conbuf; | 84 | if (sclp_conbuf) |
85 | list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue); | ||
79 | sclp_conbuf = NULL; | 86 | sclp_conbuf = NULL; |
80 | if (buffer == NULL) { | 87 | if (sclp_con_queue_running || sclp_con_suspended) |
81 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 88 | goto out_unlock; |
82 | return; | 89 | if (list_empty(&sclp_con_outqueue)) |
83 | } | 90 | goto out_unlock; |
84 | list_add_tail(&buffer->list, &sclp_con_outqueue); | 91 | buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer, |
85 | count = sclp_con_buffer_count++; | 92 | list); |
93 | sclp_con_queue_running = 1; | ||
86 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 94 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
87 | if (count) | 95 | |
88 | return; | ||
89 | rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); | 96 | rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); |
90 | if (rc) | 97 | if (rc) |
91 | sclp_conbuf_callback(buffer, rc); | 98 | sclp_conbuf_callback(buffer, rc); |
99 | return; | ||
100 | out_unlock: | ||
101 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * Wait until out queue is empty | ||
106 | */ | ||
107 | static void sclp_console_sync_queue(void) | ||
108 | { | ||
109 | unsigned long flags; | ||
110 | |||
111 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
112 | if (timer_pending(&sclp_con_timer)) | ||
113 | del_timer_sync(&sclp_con_timer); | ||
114 | while (sclp_con_queue_running) { | ||
115 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
116 | sclp_sync_wait(); | ||
117 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
118 | } | ||
119 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
92 | } | 120 | } |
93 | 121 | ||
94 | /* | 122 | /* |
@@ -123,6 +151,8 @@ sclp_console_write(struct console *console, const char *message, | |||
123 | /* make sure we have a console output buffer */ | 151 | /* make sure we have a console output buffer */ |
124 | if (sclp_conbuf == NULL) { | 152 | if (sclp_conbuf == NULL) { |
125 | while (list_empty(&sclp_con_pages)) { | 153 | while (list_empty(&sclp_con_pages)) { |
154 | if (sclp_con_suspended) | ||
155 | goto out; | ||
126 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 156 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
127 | sclp_sync_wait(); | 157 | sclp_sync_wait(); |
128 | spin_lock_irqsave(&sclp_con_lock, flags); | 158 | spin_lock_irqsave(&sclp_con_lock, flags); |
@@ -157,6 +187,7 @@ sclp_console_write(struct console *console, const char *message, | |||
157 | sclp_con_timer.expires = jiffies + HZ/10; | 187 | sclp_con_timer.expires = jiffies + HZ/10; |
158 | add_timer(&sclp_con_timer); | 188 | add_timer(&sclp_con_timer); |
159 | } | 189 | } |
190 | out: | ||
160 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 191 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
161 | } | 192 | } |
162 | 193 | ||
@@ -168,30 +199,43 @@ sclp_console_device(struct console *c, int *index) | |||
168 | } | 199 | } |
169 | 200 | ||
170 | /* | 201 | /* |
171 | * This routine is called from panic when the kernel | 202 | * Make sure that all buffers will be flushed to the SCLP. |
172 | * is going to give up. We have to make sure that all buffers | ||
173 | * will be flushed to the SCLP. | ||
174 | */ | 203 | */ |
175 | static void | 204 | static void |
176 | sclp_console_flush(void) | 205 | sclp_console_flush(void) |
177 | { | 206 | { |
207 | sclp_conbuf_emit(); | ||
208 | sclp_console_sync_queue(); | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Resume console: If there are cached messages, emit them. | ||
213 | */ | ||
214 | static void sclp_console_resume(void) | ||
215 | { | ||
178 | unsigned long flags; | 216 | unsigned long flags; |
179 | 217 | ||
218 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
219 | sclp_con_suspended = 0; | ||
220 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
180 | sclp_conbuf_emit(); | 221 | sclp_conbuf_emit(); |
222 | } | ||
223 | |||
224 | /* | ||
225 | * Suspend console: Set suspend flag and flush console | ||
226 | */ | ||
227 | static void sclp_console_suspend(void) | ||
228 | { | ||
229 | unsigned long flags; | ||
230 | |||
181 | spin_lock_irqsave(&sclp_con_lock, flags); | 231 | spin_lock_irqsave(&sclp_con_lock, flags); |
182 | if (timer_pending(&sclp_con_timer)) | 232 | sclp_con_suspended = 1; |
183 | del_timer(&sclp_con_timer); | ||
184 | while (sclp_con_buffer_count > 0) { | ||
185 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
186 | sclp_sync_wait(); | ||
187 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
188 | } | ||
189 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 233 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
234 | sclp_console_flush(); | ||
190 | } | 235 | } |
191 | 236 | ||
192 | static int | 237 | static int sclp_console_notify(struct notifier_block *self, |
193 | sclp_console_notify(struct notifier_block *self, | 238 | unsigned long event, void *data) |
194 | unsigned long event, void *data) | ||
195 | { | 239 | { |
196 | sclp_console_flush(); | 240 | sclp_console_flush(); |
197 | return NOTIFY_OK; | 241 | return NOTIFY_OK; |
@@ -199,7 +243,7 @@ sclp_console_notify(struct notifier_block *self, | |||
199 | 243 | ||
200 | static struct notifier_block on_panic_nb = { | 244 | static struct notifier_block on_panic_nb = { |
201 | .notifier_call = sclp_console_notify, | 245 | .notifier_call = sclp_console_notify, |
202 | .priority = 1, | 246 | .priority = SCLP_PANIC_PRIO_CLIENT, |
203 | }; | 247 | }; |
204 | 248 | ||
205 | static struct notifier_block on_reboot_nb = { | 249 | static struct notifier_block on_reboot_nb = { |
@@ -221,6 +265,22 @@ static struct console sclp_console = | |||
221 | }; | 265 | }; |
222 | 266 | ||
223 | /* | 267 | /* |
268 | * This function is called for SCLP suspend and resume events. | ||
269 | */ | ||
270 | void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event) | ||
271 | { | ||
272 | switch (sclp_pm_event) { | ||
273 | case SCLP_PM_EVENT_FREEZE: | ||
274 | sclp_console_suspend(); | ||
275 | break; | ||
276 | case SCLP_PM_EVENT_RESTORE: | ||
277 | case SCLP_PM_EVENT_THAW: | ||
278 | sclp_console_resume(); | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /* | ||
224 | * called by console_init() in drivers/char/tty_io.c at boot-time. | 284 | * called by console_init() in drivers/char/tty_io.c at boot-time. |
225 | */ | 285 | */ |
226 | static int __init | 286 | static int __init |
@@ -243,7 +303,6 @@ sclp_console_init(void) | |||
243 | } | 303 | } |
244 | INIT_LIST_HEAD(&sclp_con_outqueue); | 304 | INIT_LIST_HEAD(&sclp_con_outqueue); |
245 | spin_lock_init(&sclp_con_lock); | 305 | spin_lock_init(&sclp_con_lock); |
246 | sclp_con_buffer_count = 0; | ||
247 | sclp_conbuf = NULL; | 306 | sclp_conbuf = NULL; |
248 | init_timer(&sclp_con_timer); | 307 | init_timer(&sclp_con_timer); |
249 | 308 | ||
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 710af42603f8..4be63be73445 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_rw.c | 2 | * driver: reading from and writing to system console on S/390 via SCLP |
3 | * driver: reading from and writing to system console on S/390 via SCLP | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corp. 1999, 2009 |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #include <linux/kmod.h> | 10 | #include <linux/kmod.h> |
@@ -26,9 +25,16 @@ | |||
26 | */ | 25 | */ |
27 | #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) | 26 | #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) |
28 | 27 | ||
28 | static void sclp_rw_pm_event(struct sclp_register *reg, | ||
29 | enum sclp_pm_event sclp_pm_event) | ||
30 | { | ||
31 | sclp_console_pm_event(sclp_pm_event); | ||
32 | } | ||
33 | |||
29 | /* Event type structure for write message and write priority message */ | 34 | /* Event type structure for write message and write priority message */ |
30 | static struct sclp_register sclp_rw_event = { | 35 | static struct sclp_register sclp_rw_event = { |
31 | .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK | 36 | .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK, |
37 | .pm_event_fn = sclp_rw_pm_event, | ||
32 | }; | 38 | }; |
33 | 39 | ||
34 | /* | 40 | /* |
diff --git a/drivers/s390/char/sclp_rw.h b/drivers/s390/char/sclp_rw.h index 6aa7a6948bc9..85f491ea929c 100644 --- a/drivers/s390/char/sclp_rw.h +++ b/drivers/s390/char/sclp_rw.h | |||
@@ -1,11 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_rw.h | 2 | * interface to the SCLP-read/write driver |
3 | * interface to the SCLP-read/write driver | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corporation 1999, 2009 |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
9 | */ | 8 | */ |
10 | 9 | ||
11 | #ifndef __SCLP_RW_H__ | 10 | #ifndef __SCLP_RW_H__ |
@@ -93,4 +92,5 @@ void sclp_set_columns(struct sclp_buffer *, unsigned short); | |||
93 | void sclp_set_htab(struct sclp_buffer *, unsigned short); | 92 | void sclp_set_htab(struct sclp_buffer *, unsigned short); |
94 | int sclp_chars_in_buffer(struct sclp_buffer *); | 93 | int sclp_chars_in_buffer(struct sclp_buffer *); |
95 | 94 | ||
95 | void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event); | ||
96 | #endif /* __SCLP_RW_H__ */ | 96 | #endif /* __SCLP_RW_H__ */ |
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index a839aa531d7c..5518e24946aa 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c | |||
@@ -1,10 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_vt220.c | 2 | * SCLP VT220 terminal driver. |
3 | * SCLP VT220 terminal driver. | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corp. 2003, 2009 |
6 | * Copyright IBM Corp. 2003,2008 | 5 | * |
7 | * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com> | 6 | * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com> |
8 | */ | 7 | */ |
9 | 8 | ||
10 | #include <linux/module.h> | 9 | #include <linux/module.h> |
@@ -69,8 +68,11 @@ static struct list_head sclp_vt220_empty; | |||
69 | /* List of pending requests */ | 68 | /* List of pending requests */ |
70 | static struct list_head sclp_vt220_outqueue; | 69 | static struct list_head sclp_vt220_outqueue; |
71 | 70 | ||
72 | /* Number of requests in outqueue */ | 71 | /* Suspend mode flag */ |
73 | static int sclp_vt220_outqueue_count; | 72 | static int sclp_vt220_suspended; |
73 | |||
74 | /* Flag that output queue is currently running */ | ||
75 | static int sclp_vt220_queue_running; | ||
74 | 76 | ||
75 | /* Timer used for delaying write requests to merge subsequent messages into | 77 | /* Timer used for delaying write requests to merge subsequent messages into |
76 | * a single buffer */ | 78 | * a single buffer */ |
@@ -92,6 +94,8 @@ static int __initdata sclp_vt220_init_count; | |||
92 | static int sclp_vt220_flush_later; | 94 | static int sclp_vt220_flush_later; |
93 | 95 | ||
94 | static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf); | 96 | static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf); |
97 | static void sclp_vt220_pm_event_fn(struct sclp_register *reg, | ||
98 | enum sclp_pm_event sclp_pm_event); | ||
95 | static int __sclp_vt220_emit(struct sclp_vt220_request *request); | 99 | static int __sclp_vt220_emit(struct sclp_vt220_request *request); |
96 | static void sclp_vt220_emit_current(void); | 100 | static void sclp_vt220_emit_current(void); |
97 | 101 | ||
@@ -100,7 +104,8 @@ static struct sclp_register sclp_vt220_register = { | |||
100 | .send_mask = EVTYP_VT220MSG_MASK, | 104 | .send_mask = EVTYP_VT220MSG_MASK, |
101 | .receive_mask = EVTYP_VT220MSG_MASK, | 105 | .receive_mask = EVTYP_VT220MSG_MASK, |
102 | .state_change_fn = NULL, | 106 | .state_change_fn = NULL, |
103 | .receiver_fn = sclp_vt220_receiver_fn | 107 | .receiver_fn = sclp_vt220_receiver_fn, |
108 | .pm_event_fn = sclp_vt220_pm_event_fn, | ||
104 | }; | 109 | }; |
105 | 110 | ||
106 | 111 | ||
@@ -120,15 +125,19 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) | |||
120 | spin_lock_irqsave(&sclp_vt220_lock, flags); | 125 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
121 | /* Move request from outqueue to empty queue */ | 126 | /* Move request from outqueue to empty queue */ |
122 | list_del(&request->list); | 127 | list_del(&request->list); |
123 | sclp_vt220_outqueue_count--; | ||
124 | list_add_tail((struct list_head *) page, &sclp_vt220_empty); | 128 | list_add_tail((struct list_head *) page, &sclp_vt220_empty); |
125 | /* Check if there is a pending buffer on the out queue. */ | 129 | /* Check if there is a pending buffer on the out queue. */ |
126 | request = NULL; | 130 | request = NULL; |
127 | if (!list_empty(&sclp_vt220_outqueue)) | 131 | if (!list_empty(&sclp_vt220_outqueue)) |
128 | request = list_entry(sclp_vt220_outqueue.next, | 132 | request = list_entry(sclp_vt220_outqueue.next, |
129 | struct sclp_vt220_request, list); | 133 | struct sclp_vt220_request, list); |
134 | if (!request || sclp_vt220_suspended) { | ||
135 | sclp_vt220_queue_running = 0; | ||
136 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | ||
137 | break; | ||
138 | } | ||
130 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | 139 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
131 | } while (request && __sclp_vt220_emit(request)); | 140 | } while (__sclp_vt220_emit(request)); |
132 | if (request == NULL && sclp_vt220_flush_later) | 141 | if (request == NULL && sclp_vt220_flush_later) |
133 | sclp_vt220_emit_current(); | 142 | sclp_vt220_emit_current(); |
134 | /* Check if the tty needs a wake up call */ | 143 | /* Check if the tty needs a wake up call */ |
@@ -212,26 +221,7 @@ __sclp_vt220_emit(struct sclp_vt220_request *request) | |||
212 | } | 221 | } |
213 | 222 | ||
214 | /* | 223 | /* |
215 | * Queue and emit given request. | 224 | * Queue and emit current request. |
216 | */ | ||
217 | static void | ||
218 | sclp_vt220_emit(struct sclp_vt220_request *request) | ||
219 | { | ||
220 | unsigned long flags; | ||
221 | int count; | ||
222 | |||
223 | spin_lock_irqsave(&sclp_vt220_lock, flags); | ||
224 | list_add_tail(&request->list, &sclp_vt220_outqueue); | ||
225 | count = sclp_vt220_outqueue_count++; | ||
226 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | ||
227 | /* Emit only the first buffer immediately - callback takes care of | ||
228 | * the rest */ | ||
229 | if (count == 0 && __sclp_vt220_emit(request)) | ||
230 | sclp_vt220_process_queue(request); | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * Queue and emit current request. Return zero on success, non-zero otherwise. | ||
235 | */ | 225 | */ |
236 | static void | 226 | static void |
237 | sclp_vt220_emit_current(void) | 227 | sclp_vt220_emit_current(void) |
@@ -241,22 +231,33 @@ sclp_vt220_emit_current(void) | |||
241 | struct sclp_vt220_sccb *sccb; | 231 | struct sclp_vt220_sccb *sccb; |
242 | 232 | ||
243 | spin_lock_irqsave(&sclp_vt220_lock, flags); | 233 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
244 | request = NULL; | 234 | if (sclp_vt220_current_request) { |
245 | if (sclp_vt220_current_request != NULL) { | ||
246 | sccb = (struct sclp_vt220_sccb *) | 235 | sccb = (struct sclp_vt220_sccb *) |
247 | sclp_vt220_current_request->sclp_req.sccb; | 236 | sclp_vt220_current_request->sclp_req.sccb; |
248 | /* Only emit buffers with content */ | 237 | /* Only emit buffers with content */ |
249 | if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) { | 238 | if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) { |
250 | request = sclp_vt220_current_request; | 239 | list_add_tail(&sclp_vt220_current_request->list, |
240 | &sclp_vt220_outqueue); | ||
251 | sclp_vt220_current_request = NULL; | 241 | sclp_vt220_current_request = NULL; |
252 | if (timer_pending(&sclp_vt220_timer)) | 242 | if (timer_pending(&sclp_vt220_timer)) |
253 | del_timer(&sclp_vt220_timer); | 243 | del_timer(&sclp_vt220_timer); |
254 | } | 244 | } |
255 | sclp_vt220_flush_later = 0; | 245 | sclp_vt220_flush_later = 0; |
256 | } | 246 | } |
247 | if (sclp_vt220_queue_running || sclp_vt220_suspended) | ||
248 | goto out_unlock; | ||
249 | if (list_empty(&sclp_vt220_outqueue)) | ||
250 | goto out_unlock; | ||
251 | request = list_first_entry(&sclp_vt220_outqueue, | ||
252 | struct sclp_vt220_request, list); | ||
253 | sclp_vt220_queue_running = 1; | ||
254 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | ||
255 | |||
256 | if (__sclp_vt220_emit(request)) | ||
257 | sclp_vt220_process_queue(request); | ||
258 | return; | ||
259 | out_unlock: | ||
257 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | 260 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
258 | if (request != NULL) | ||
259 | sclp_vt220_emit(request); | ||
260 | } | 261 | } |
261 | 262 | ||
262 | #define SCLP_NORMAL_WRITE 0x00 | 263 | #define SCLP_NORMAL_WRITE 0x00 |
@@ -396,7 +397,7 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, | |||
396 | if (sclp_vt220_current_request == NULL) { | 397 | if (sclp_vt220_current_request == NULL) { |
397 | while (list_empty(&sclp_vt220_empty)) { | 398 | while (list_empty(&sclp_vt220_empty)) { |
398 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | 399 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
399 | if (may_fail) | 400 | if (may_fail || sclp_vt220_suspended) |
400 | goto out; | 401 | goto out; |
401 | else | 402 | else |
402 | sclp_sync_wait(); | 403 | sclp_sync_wait(); |
@@ -531,7 +532,7 @@ sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) | |||
531 | static void | 532 | static void |
532 | sclp_vt220_flush_chars(struct tty_struct *tty) | 533 | sclp_vt220_flush_chars(struct tty_struct *tty) |
533 | { | 534 | { |
534 | if (sclp_vt220_outqueue_count == 0) | 535 | if (!sclp_vt220_queue_running) |
535 | sclp_vt220_emit_current(); | 536 | sclp_vt220_emit_current(); |
536 | else | 537 | else |
537 | sclp_vt220_flush_later = 1; | 538 | sclp_vt220_flush_later = 1; |
@@ -635,7 +636,6 @@ static int __init __sclp_vt220_init(int num_pages) | |||
635 | init_timer(&sclp_vt220_timer); | 636 | init_timer(&sclp_vt220_timer); |
636 | sclp_vt220_current_request = NULL; | 637 | sclp_vt220_current_request = NULL; |
637 | sclp_vt220_buffered_chars = 0; | 638 | sclp_vt220_buffered_chars = 0; |
638 | sclp_vt220_outqueue_count = 0; | ||
639 | sclp_vt220_tty = NULL; | 639 | sclp_vt220_tty = NULL; |
640 | sclp_vt220_flush_later = 0; | 640 | sclp_vt220_flush_later = 0; |
641 | 641 | ||
@@ -736,7 +736,7 @@ static void __sclp_vt220_flush_buffer(void) | |||
736 | spin_lock_irqsave(&sclp_vt220_lock, flags); | 736 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
737 | if (timer_pending(&sclp_vt220_timer)) | 737 | if (timer_pending(&sclp_vt220_timer)) |
738 | del_timer(&sclp_vt220_timer); | 738 | del_timer(&sclp_vt220_timer); |
739 | while (sclp_vt220_outqueue_count > 0) { | 739 | while (sclp_vt220_queue_running) { |
740 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | 740 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
741 | sclp_sync_wait(); | 741 | sclp_sync_wait(); |
742 | spin_lock_irqsave(&sclp_vt220_lock, flags); | 742 | spin_lock_irqsave(&sclp_vt220_lock, flags); |
@@ -744,6 +744,46 @@ static void __sclp_vt220_flush_buffer(void) | |||
744 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | 744 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); |
745 | } | 745 | } |
746 | 746 | ||
747 | /* | ||
748 | * Resume console: If there are cached messages, emit them. | ||
749 | */ | ||
750 | static void sclp_vt220_resume(void) | ||
751 | { | ||
752 | unsigned long flags; | ||
753 | |||
754 | spin_lock_irqsave(&sclp_vt220_lock, flags); | ||
755 | sclp_vt220_suspended = 0; | ||
756 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | ||
757 | sclp_vt220_emit_current(); | ||
758 | } | ||
759 | |||
760 | /* | ||
761 | * Suspend console: Set suspend flag and flush console | ||
762 | */ | ||
763 | static void sclp_vt220_suspend(void) | ||
764 | { | ||
765 | unsigned long flags; | ||
766 | |||
767 | spin_lock_irqsave(&sclp_vt220_lock, flags); | ||
768 | sclp_vt220_suspended = 1; | ||
769 | spin_unlock_irqrestore(&sclp_vt220_lock, flags); | ||
770 | __sclp_vt220_flush_buffer(); | ||
771 | } | ||
772 | |||
773 | static void sclp_vt220_pm_event_fn(struct sclp_register *reg, | ||
774 | enum sclp_pm_event sclp_pm_event) | ||
775 | { | ||
776 | switch (sclp_pm_event) { | ||
777 | case SCLP_PM_EVENT_FREEZE: | ||
778 | sclp_vt220_suspend(); | ||
779 | break; | ||
780 | case SCLP_PM_EVENT_RESTORE: | ||
781 | case SCLP_PM_EVENT_THAW: | ||
782 | sclp_vt220_resume(); | ||
783 | break; | ||
784 | } | ||
785 | } | ||
786 | |||
747 | static int | 787 | static int |
748 | sclp_vt220_notify(struct notifier_block *self, | 788 | sclp_vt220_notify(struct notifier_block *self, |
749 | unsigned long event, void *data) | 789 | unsigned long event, void *data) |
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 5469e099597e..a26333774701 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h | |||
@@ -3,7 +3,7 @@ | |||
3 | * tape device driver for 3480/3490E/3590 tapes. | 3 | * tape device driver for 3480/3490E/3590 tapes. |
4 | * | 4 | * |
5 | * S390 and zSeries version | 5 | * S390 and zSeries version |
6 | * Copyright IBM Corp. 2001,2006 | 6 | * Copyright IBM Corp. 2001, 2009 |
7 | * Author(s): Carsten Otte <cotte@de.ibm.com> | 7 | * Author(s): Carsten Otte <cotte@de.ibm.com> |
8 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> | 8 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> |
9 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 9 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
@@ -286,6 +286,7 @@ extern void tape_state_set(struct tape_device *, enum tape_state); | |||
286 | 286 | ||
287 | extern int tape_generic_online(struct tape_device *, struct tape_discipline *); | 287 | extern int tape_generic_online(struct tape_device *, struct tape_discipline *); |
288 | extern int tape_generic_offline(struct ccw_device *); | 288 | extern int tape_generic_offline(struct ccw_device *); |
289 | extern int tape_generic_pm_suspend(struct ccw_device *); | ||
289 | 290 | ||
290 | /* Externals from tape_devmap.c */ | 291 | /* Externals from tape_devmap.c */ |
291 | extern int tape_generic_probe(struct ccw_device *); | 292 | extern int tape_generic_probe(struct ccw_device *); |
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 2d00a383a475..144d2a5e1a92 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * drivers/s390/char/tape_34xx.c | 2 | * drivers/s390/char/tape_34xx.c |
3 | * tape device discipline for 3480/3490 tapes. | 3 | * tape device discipline for 3480/3490 tapes. |
4 | * | 4 | * |
5 | * Copyright (C) IBM Corp. 2001,2006 | 5 | * Copyright IBM Corp. 2001, 2009 |
6 | * Author(s): Carsten Otte <cotte@de.ibm.com> | 6 | * Author(s): Carsten Otte <cotte@de.ibm.com> |
7 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> | 7 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
@@ -1302,6 +1302,7 @@ static struct ccw_driver tape_34xx_driver = { | |||
1302 | .remove = tape_generic_remove, | 1302 | .remove = tape_generic_remove, |
1303 | .set_online = tape_34xx_online, | 1303 | .set_online = tape_34xx_online, |
1304 | .set_offline = tape_generic_offline, | 1304 | .set_offline = tape_generic_offline, |
1305 | .freeze = tape_generic_pm_suspend, | ||
1305 | }; | 1306 | }; |
1306 | 1307 | ||
1307 | static int | 1308 | static int |
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index c453b2f3e9f4..23e6598bc4b5 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * drivers/s390/char/tape_3590.c | 2 | * drivers/s390/char/tape_3590.c |
3 | * tape device discipline for 3590 tapes. | 3 | * tape device discipline for 3590 tapes. |
4 | * | 4 | * |
5 | * Copyright IBM Corp. 2001,2006 | 5 | * Copyright IBM Corp. 2001, 2009 |
6 | * Author(s): Stefan Bader <shbader@de.ibm.com> | 6 | * Author(s): Stefan Bader <shbader@de.ibm.com> |
7 | * Michael Holzheu <holzheu@de.ibm.com> | 7 | * Michael Holzheu <holzheu@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
@@ -1715,6 +1715,7 @@ static struct ccw_driver tape_3590_driver = { | |||
1715 | .remove = tape_generic_remove, | 1715 | .remove = tape_generic_remove, |
1716 | .set_offline = tape_generic_offline, | 1716 | .set_offline = tape_generic_offline, |
1717 | .set_online = tape_3590_online, | 1717 | .set_online = tape_3590_online, |
1718 | .freeze = tape_generic_pm_suspend, | ||
1718 | }; | 1719 | }; |
1719 | 1720 | ||
1720 | /* | 1721 | /* |
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 8a109f3b69c6..3ebaa8eb5c86 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * basic function of the tape device driver | 3 | * basic function of the tape device driver |
4 | * | 4 | * |
5 | * S390 and zSeries version | 5 | * S390 and zSeries version |
6 | * Copyright IBM Corp. 2001,2006 | 6 | * Copyright IBM Corp. 2001, 2009 |
7 | * Author(s): Carsten Otte <cotte@de.ibm.com> | 7 | * Author(s): Carsten Otte <cotte@de.ibm.com> |
8 | * Michael Holzheu <holzheu@de.ibm.com> | 8 | * Michael Holzheu <holzheu@de.ibm.com> |
9 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> | 9 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> |
@@ -380,6 +380,55 @@ tape_cleanup_device(struct tape_device *device) | |||
380 | } | 380 | } |
381 | 381 | ||
382 | /* | 382 | /* |
383 | * Suspend device. | ||
384 | * | ||
385 | * Called by the common I/O layer if the drive should be suspended on user | ||
386 | * request. We refuse to suspend if the device is loaded or in use for the | ||
387 | * following reason: | ||
388 | * While the Linux guest is suspended, it might be logged off which causes | ||
389 | * devices to be detached. Tape devices are automatically rewound and unloaded | ||
390 | * during DETACH processing (unless the tape device was attached with the | ||
391 | * NOASSIGN or MULTIUSER option). After rewind/unload, there is no way to | ||
392 | * resume the original state of the tape device, since we would need to | ||
393 | * manually re-load the cartridge which was active at suspend time. | ||
394 | */ | ||
395 | int tape_generic_pm_suspend(struct ccw_device *cdev) | ||
396 | { | ||
397 | struct tape_device *device; | ||
398 | |||
399 | device = cdev->dev.driver_data; | ||
400 | if (!device) { | ||
401 | return -ENODEV; | ||
402 | } | ||
403 | |||
404 | DBF_LH(3, "(%08x): tape_generic_pm_suspend(%p)\n", | ||
405 | device->cdev_id, device); | ||
406 | |||
407 | if (device->medium_state != MS_UNLOADED) { | ||
408 | pr_err("A cartridge is loaded in tape device %s, " | ||
409 | "refusing to suspend\n", dev_name(&cdev->dev)); | ||
410 | return -EBUSY; | ||
411 | } | ||
412 | |||
413 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
414 | switch (device->tape_state) { | ||
415 | case TS_INIT: | ||
416 | case TS_NOT_OPER: | ||
417 | case TS_UNUSED: | ||
418 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
419 | break; | ||
420 | default: | ||
421 | pr_err("Tape device %s is busy, refusing to " | ||
422 | "suspend\n", dev_name(&cdev->dev)); | ||
423 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
424 | return -EBUSY; | ||
425 | } | ||
426 | |||
427 | DBF_LH(3, "(%08x): Drive suspended.\n", device->cdev_id); | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | /* | ||
383 | * Set device offline. | 432 | * Set device offline. |
384 | * | 433 | * |
385 | * Called by the common I/O layer if the drive should set offline on user | 434 | * Called by the common I/O layer if the drive should set offline on user |
@@ -1273,6 +1322,7 @@ EXPORT_SYMBOL(tape_generic_remove); | |||
1273 | EXPORT_SYMBOL(tape_generic_probe); | 1322 | EXPORT_SYMBOL(tape_generic_probe); |
1274 | EXPORT_SYMBOL(tape_generic_online); | 1323 | EXPORT_SYMBOL(tape_generic_online); |
1275 | EXPORT_SYMBOL(tape_generic_offline); | 1324 | EXPORT_SYMBOL(tape_generic_offline); |
1325 | EXPORT_SYMBOL(tape_generic_pm_suspend); | ||
1276 | EXPORT_SYMBOL(tape_put_device); | 1326 | EXPORT_SYMBOL(tape_put_device); |
1277 | EXPORT_SYMBOL(tape_get_device_reference); | 1327 | EXPORT_SYMBOL(tape_get_device_reference); |
1278 | EXPORT_SYMBOL(tape_state_verbose); | 1328 | EXPORT_SYMBOL(tape_state_verbose); |
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index d8a2289fcb69..e925808c2149 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * character device driver for reading z/VM system service records | 3 | * character device driver for reading z/VM system service records |
4 | * | 4 | * |
5 | * | 5 | * |
6 | * Copyright 2004 IBM Corporation | 6 | * Copyright IBM Corp. 2004, 2009 |
7 | * character device driver for reading z/VM system service records, | 7 | * character device driver for reading z/VM system service records, |
8 | * Version 1.0 | 8 | * Version 1.0 |
9 | * Author(s): Xenia Tkatschow <xenia@us.ibm.com> | 9 | * Author(s): Xenia Tkatschow <xenia@us.ibm.com> |
@@ -660,6 +660,29 @@ static struct attribute *vmlogrdr_attrs[] = { | |||
660 | NULL, | 660 | NULL, |
661 | }; | 661 | }; |
662 | 662 | ||
663 | static int vmlogrdr_pm_prepare(struct device *dev) | ||
664 | { | ||
665 | int rc; | ||
666 | struct vmlogrdr_priv_t *priv = dev->driver_data; | ||
667 | |||
668 | rc = 0; | ||
669 | if (priv) { | ||
670 | spin_lock_bh(&priv->priv_lock); | ||
671 | if (priv->dev_in_use) | ||
672 | rc = -EBUSY; | ||
673 | spin_unlock_bh(&priv->priv_lock); | ||
674 | } | ||
675 | if (rc) | ||
676 | pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n", | ||
677 | dev_name(dev)); | ||
678 | return rc; | ||
679 | } | ||
680 | |||
681 | |||
682 | static struct dev_pm_ops vmlogrdr_pm_ops = { | ||
683 | .prepare = vmlogrdr_pm_prepare, | ||
684 | }; | ||
685 | |||
663 | static struct attribute_group vmlogrdr_attr_group = { | 686 | static struct attribute_group vmlogrdr_attr_group = { |
664 | .attrs = vmlogrdr_attrs, | 687 | .attrs = vmlogrdr_attrs, |
665 | }; | 688 | }; |
@@ -668,6 +691,7 @@ static struct class *vmlogrdr_class; | |||
668 | static struct device_driver vmlogrdr_driver = { | 691 | static struct device_driver vmlogrdr_driver = { |
669 | .name = "vmlogrdr", | 692 | .name = "vmlogrdr", |
670 | .bus = &iucv_bus, | 693 | .bus = &iucv_bus, |
694 | .pm = &vmlogrdr_pm_ops, | ||
671 | }; | 695 | }; |
672 | 696 | ||
673 | 697 | ||
@@ -729,6 +753,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) | |||
729 | dev->bus = &iucv_bus; | 753 | dev->bus = &iucv_bus; |
730 | dev->parent = iucv_root; | 754 | dev->parent = iucv_root; |
731 | dev->driver = &vmlogrdr_driver; | 755 | dev->driver = &vmlogrdr_driver; |
756 | dev->driver_data = priv; | ||
732 | /* | 757 | /* |
733 | * The release function could be called after the | 758 | * The release function could be called after the |
734 | * module has been unloaded. It's _only_ task is to | 759 | * module has been unloaded. It's _only_ task is to |
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 5dcef81fc9d9..92458219a9e9 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Linux driver for System z and s390 unit record devices | 2 | * Linux driver for System z and s390 unit record devices |
3 | * (z/VM virtual punch, reader, printer) | 3 | * (z/VM virtual punch, reader, printer) |
4 | * | 4 | * |
5 | * Copyright IBM Corp. 2001, 2007 | 5 | * Copyright IBM Corp. 2001, 2009 |
6 | * Authors: Malcolm Beattie <beattiem@uk.ibm.com> | 6 | * Authors: Malcolm Beattie <beattiem@uk.ibm.com> |
7 | * Michael Holzheu <holzheu@de.ibm.com> | 7 | * Michael Holzheu <holzheu@de.ibm.com> |
8 | * Frank Munzert <munzert@de.ibm.com> | 8 | * Frank Munzert <munzert@de.ibm.com> |
@@ -60,6 +60,7 @@ static int ur_probe(struct ccw_device *cdev); | |||
60 | static void ur_remove(struct ccw_device *cdev); | 60 | static void ur_remove(struct ccw_device *cdev); |
61 | static int ur_set_online(struct ccw_device *cdev); | 61 | static int ur_set_online(struct ccw_device *cdev); |
62 | static int ur_set_offline(struct ccw_device *cdev); | 62 | static int ur_set_offline(struct ccw_device *cdev); |
63 | static int ur_pm_suspend(struct ccw_device *cdev); | ||
63 | 64 | ||
64 | static struct ccw_driver ur_driver = { | 65 | static struct ccw_driver ur_driver = { |
65 | .name = "vmur", | 66 | .name = "vmur", |
@@ -69,6 +70,7 @@ static struct ccw_driver ur_driver = { | |||
69 | .remove = ur_remove, | 70 | .remove = ur_remove, |
70 | .set_online = ur_set_online, | 71 | .set_online = ur_set_online, |
71 | .set_offline = ur_set_offline, | 72 | .set_offline = ur_set_offline, |
73 | .freeze = ur_pm_suspend, | ||
72 | }; | 74 | }; |
73 | 75 | ||
74 | static DEFINE_MUTEX(vmur_mutex); | 76 | static DEFINE_MUTEX(vmur_mutex); |
@@ -158,6 +160,28 @@ static void urdev_put(struct urdev *urd) | |||
158 | } | 160 | } |
159 | 161 | ||
160 | /* | 162 | /* |
163 | * State and contents of ur devices can be changed by class D users issuing | ||
164 | * CP commands such as PURGE or TRANSFER, while the Linux guest is suspended. | ||
165 | * Also the Linux guest might be logged off, which causes all active spool | ||
166 | * files to be closed. | ||
167 | * So we cannot guarantee that spool files are still the same when the Linux | ||
168 | * guest is resumed. In order to avoid unpredictable results at resume time | ||
169 | * we simply refuse to suspend if a ur device node is open. | ||
170 | */ | ||
171 | static int ur_pm_suspend(struct ccw_device *cdev) | ||
172 | { | ||
173 | struct urdev *urd = cdev->dev.driver_data; | ||
174 | |||
175 | TRACE("ur_pm_suspend: cdev=%p\n", cdev); | ||
176 | if (urd->open_flag) { | ||
177 | pr_err("Unit record device %s is busy, %s refusing to " | ||
178 | "suspend.\n", dev_name(&cdev->dev), ur_banner); | ||
179 | return -EBUSY; | ||
180 | } | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /* | ||
161 | * Low-level functions to do I/O to a ur device. | 185 | * Low-level functions to do I/O to a ur device. |
162 | * alloc_chan_prog | 186 | * alloc_chan_prog |
163 | * free_chan_prog | 187 | * free_chan_prog |
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 21a2a829bf4e..cb7854c10c04 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c | |||
@@ -1,17 +1,23 @@ | |||
1 | /* | 1 | /* |
2 | * Watchdog implementation based on z/VM Watchdog Timer API | 2 | * Watchdog implementation based on z/VM Watchdog Timer API |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2004,2009 | ||
5 | * | ||
4 | * The user space watchdog daemon can use this driver as | 6 | * The user space watchdog daemon can use this driver as |
5 | * /dev/vmwatchdog to have z/VM execute the specified CP | 7 | * /dev/vmwatchdog to have z/VM execute the specified CP |
6 | * command when the timeout expires. The default command is | 8 | * command when the timeout expires. The default command is |
7 | * "IPL", which which cause an immediate reboot. | 9 | * "IPL", which which cause an immediate reboot. |
8 | */ | 10 | */ |
11 | #define KMSG_COMPONENT "vmwatchdog" | ||
12 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
13 | |||
9 | #include <linux/init.h> | 14 | #include <linux/init.h> |
10 | #include <linux/fs.h> | 15 | #include <linux/fs.h> |
11 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
12 | #include <linux/miscdevice.h> | 17 | #include <linux/miscdevice.h> |
13 | #include <linux/module.h> | 18 | #include <linux/module.h> |
14 | #include <linux/moduleparam.h> | 19 | #include <linux/moduleparam.h> |
20 | #include <linux/suspend.h> | ||
15 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
16 | #include <linux/smp_lock.h> | 22 | #include <linux/smp_lock.h> |
17 | 23 | ||
@@ -43,6 +49,9 @@ static unsigned int vmwdt_interval = 60; | |||
43 | static unsigned long vmwdt_is_open; | 49 | static unsigned long vmwdt_is_open; |
44 | static int vmwdt_expect_close; | 50 | static int vmwdt_expect_close; |
45 | 51 | ||
52 | #define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ | ||
53 | #define VMWDT_RUNNING 1 /* The watchdog is armed */ | ||
54 | |||
46 | enum vmwdt_func { | 55 | enum vmwdt_func { |
47 | /* function codes */ | 56 | /* function codes */ |
48 | wdt_init = 0, | 57 | wdt_init = 0, |
@@ -92,6 +101,7 @@ static int vmwdt_keepalive(void) | |||
92 | EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); | 101 | EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); |
93 | 102 | ||
94 | func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; | 103 | func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; |
104 | set_bit(VMWDT_RUNNING, &vmwdt_is_open); | ||
95 | ret = __diag288(func, vmwdt_interval, ebc_cmd, len); | 105 | ret = __diag288(func, vmwdt_interval, ebc_cmd, len); |
96 | WARN_ON(ret != 0); | 106 | WARN_ON(ret != 0); |
97 | kfree(ebc_cmd); | 107 | kfree(ebc_cmd); |
@@ -102,6 +112,7 @@ static int vmwdt_disable(void) | |||
102 | { | 112 | { |
103 | int ret = __diag288(wdt_cancel, 0, "", 0); | 113 | int ret = __diag288(wdt_cancel, 0, "", 0); |
104 | WARN_ON(ret != 0); | 114 | WARN_ON(ret != 0); |
115 | clear_bit(VMWDT_RUNNING, &vmwdt_is_open); | ||
105 | return ret; | 116 | return ret; |
106 | } | 117 | } |
107 | 118 | ||
@@ -123,13 +134,13 @@ static int vmwdt_open(struct inode *i, struct file *f) | |||
123 | { | 134 | { |
124 | int ret; | 135 | int ret; |
125 | lock_kernel(); | 136 | lock_kernel(); |
126 | if (test_and_set_bit(0, &vmwdt_is_open)) { | 137 | if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { |
127 | unlock_kernel(); | 138 | unlock_kernel(); |
128 | return -EBUSY; | 139 | return -EBUSY; |
129 | } | 140 | } |
130 | ret = vmwdt_keepalive(); | 141 | ret = vmwdt_keepalive(); |
131 | if (ret) | 142 | if (ret) |
132 | clear_bit(0, &vmwdt_is_open); | 143 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); |
133 | unlock_kernel(); | 144 | unlock_kernel(); |
134 | return ret ? ret : nonseekable_open(i, f); | 145 | return ret ? ret : nonseekable_open(i, f); |
135 | } | 146 | } |
@@ -139,7 +150,7 @@ static int vmwdt_close(struct inode *i, struct file *f) | |||
139 | if (vmwdt_expect_close == 42) | 150 | if (vmwdt_expect_close == 42) |
140 | vmwdt_disable(); | 151 | vmwdt_disable(); |
141 | vmwdt_expect_close = 0; | 152 | vmwdt_expect_close = 0; |
142 | clear_bit(0, &vmwdt_is_open); | 153 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); |
143 | return 0; | 154 | return 0; |
144 | } | 155 | } |
145 | 156 | ||
@@ -223,6 +234,57 @@ static ssize_t vmwdt_write(struct file *f, const char __user *buf, | |||
223 | return count; | 234 | return count; |
224 | } | 235 | } |
225 | 236 | ||
237 | static int vmwdt_resume(void) | ||
238 | { | ||
239 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); | ||
240 | return NOTIFY_DONE; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * It makes no sense to go into suspend while the watchdog is running. | ||
245 | * Depending on the memory size, the watchdog might trigger, while we | ||
246 | * are still saving the memory. | ||
247 | * We reuse the open flag to ensure that suspend and watchdog open are | ||
248 | * exclusive operations | ||
249 | */ | ||
250 | static int vmwdt_suspend(void) | ||
251 | { | ||
252 | if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { | ||
253 | pr_err("The watchdog is in use. " | ||
254 | "This prevents hibernation or suspend.\n"); | ||
255 | return NOTIFY_BAD; | ||
256 | } | ||
257 | if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { | ||
258 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); | ||
259 | pr_err("The watchdog is running. " | ||
260 | "This prevents hibernation or suspend.\n"); | ||
261 | return NOTIFY_BAD; | ||
262 | } | ||
263 | return NOTIFY_DONE; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * This function is called for suspend and resume. | ||
268 | */ | ||
269 | static int vmwdt_power_event(struct notifier_block *this, unsigned long event, | ||
270 | void *ptr) | ||
271 | { | ||
272 | switch (event) { | ||
273 | case PM_POST_HIBERNATION: | ||
274 | case PM_POST_SUSPEND: | ||
275 | return vmwdt_resume(); | ||
276 | case PM_HIBERNATION_PREPARE: | ||
277 | case PM_SUSPEND_PREPARE: | ||
278 | return vmwdt_suspend(); | ||
279 | default: | ||
280 | return NOTIFY_DONE; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | static struct notifier_block vmwdt_power_notifier = { | ||
285 | .notifier_call = vmwdt_power_event, | ||
286 | }; | ||
287 | |||
226 | static const struct file_operations vmwdt_fops = { | 288 | static const struct file_operations vmwdt_fops = { |
227 | .open = &vmwdt_open, | 289 | .open = &vmwdt_open, |
228 | .release = &vmwdt_close, | 290 | .release = &vmwdt_close, |
@@ -244,12 +306,21 @@ static int __init vmwdt_init(void) | |||
244 | ret = vmwdt_probe(); | 306 | ret = vmwdt_probe(); |
245 | if (ret) | 307 | if (ret) |
246 | return ret; | 308 | return ret; |
247 | return misc_register(&vmwdt_dev); | 309 | ret = register_pm_notifier(&vmwdt_power_notifier); |
310 | if (ret) | ||
311 | return ret; | ||
312 | ret = misc_register(&vmwdt_dev); | ||
313 | if (ret) { | ||
314 | unregister_pm_notifier(&vmwdt_power_notifier); | ||
315 | return ret; | ||
316 | } | ||
317 | return 0; | ||
248 | } | 318 | } |
249 | module_init(vmwdt_init); | 319 | module_init(vmwdt_init); |
250 | 320 | ||
251 | static void __exit vmwdt_exit(void) | 321 | static void __exit vmwdt_exit(void) |
252 | { | 322 | { |
253 | WARN_ON(misc_deregister(&vmwdt_dev) != 0); | 323 | unregister_pm_notifier(&vmwdt_power_notifier); |
324 | misc_deregister(&vmwdt_dev); | ||
254 | } | 325 | } |
255 | module_exit(vmwdt_exit); | 326 | module_exit(vmwdt_exit); |