Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/con3215: Drop console data printout when buffer full

Using z/VM the 3270 terminal emulator also emulates an IBM 3215 console
which outputs line by line. When the screen is full, the console enters
the MORE... state and waits for the operator to confirm the data
on the screen by pressing a clear key. If this does not happen in the
default time frame (currently 50 seconds) the console enters the HOLDING
state.
It then waits another time frame (currently 10 seconds) before the output
continues on the next screen. When the operator presses the clear key
during these wait times, the output continues immediately.

This may lead to a very long boot time when the console
has to print many messages, also the system may hang because of the
console's limited buffer space and the system waits for the console
output to drain and finally to finish. This problem can only occur
when a terminal emulator is actually connected to the 3215 console
driver. If not z/VM simply drops console output.

Remedy this rare situation and add a kernel boot command line parameter
con3215_drop. It can be set to 0 (do not drop) or 1 (do drop) which is
the default. This instructs the kernel drop console data when the
console buffer is full. This speeds up the boot time considerable and
also does not hang the system anymore.

Add a sysfs attribute file for console IBM 3215 named con_drop.
This allows for changing the behavior after the boot, for example when
during interactive debugging a panic/crash is expected.

Here is a test of the new behavior using the following test program:
#/bin/bash
declare -i cnt=4

mode=$(cat /sys/bus/ccw/drivers/3215/con_drop)
[ $mode = yes ] && cnt=25

echo "cons_drop $(cat /sys/bus/ccw/drivers/3215/con_drop)"
echo "vmcp term more 5 2"
vmcp term more 5 2
echo "Run $cnt iterations of "'echo t > /proc/sysrq-trigger'

for i in $(seq $cnt)
do
echo "$i. command 'echo t > /proc/sysrq-trigger' at $(date +%F,%T)"
echo t > /proc/sysrq-trigger
sleep 1
done
echo "droptest done" > /dev/kmsg
#

Output with sysfs attribute con_drop set to 1:
# ./droptest.sh
cons_drop yes
vmcp term more 5 2
Run 25 iterations of echo t > /proc/sysrq-trigger
1. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:09
2. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:10
3. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:11
4. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:12
5. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:13
6. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:14
7. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:15
8. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:16
9. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:17
10. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:18
11. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:19
12. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:20
13. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:21
14. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:22
15. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:23
16. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:24
17. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:25
18. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:26
19. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:27
20. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:28
21. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:29
22. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:30
23. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:31
24. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:32
25. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:15:33
#

There are no hangs anymore.

Output with sysfs attribute con_drop set to 0 and identical
setting for z/VM console 'term more 5 2'. Sometimes hitting the
clear key at the x3270 console to progress output.

# ./droptest.sh
cons_drop no
vmcp term more 5 2
Run 4 iterations of echo t > /proc/sysrq-trigger
1. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:20:58
2. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:24:32
3. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:28:04
4. command 'echo t > /proc/sysrq-trigger' at 2022-09-02,10:31:37
#

Details:
Enable function raw3215_write() to handle tab expansion and newlines
and feed it with input not larger than the console buffer of 65536
bytes. Function raw3125_putchar() just forwards its character for
output to raw3215_write().

This moves tab to blank conversion to one function raw3215_write()
which also does call raw3215_make_room() to wait for enough free
buffer space.

Function handle_write() loops over all its input and segments input
into chunks of console buffer size (should the input be larger).

Rework tab expansion handling logic to avoid code duplication.

Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Acked-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>

authored by

Thomas Richter and committed by
Vasily Gorbik
1f3307cf 655ae931

+172 -67
+11
Documentation/admin-guide/kernel-parameters.txt
··· 703 703 condev= [HW,S390] console device 704 704 conmode= 705 705 706 + con3215_drop= [S390] 3215 console drop mode. 707 + Format: y|n|Y|N|1|0 708 + When set to true, drop data on the 3215 console when 709 + the console buffer is full. In this case the 710 + operator using a 3270 terminal emulator (for example 711 + x3270) does not have to enter the clear key for the 712 + console output to advance and the kernel to continue. 713 + This leads to a much faster boot time when a 3270 714 + terminal emulator is active. If no 3270 terminal 715 + emulator is used, this parameter has no effect. 716 + 706 717 console= [KNL] Output console device and options. 707 718 708 719 tty<n> Use the virtual console device <n>.
+161 -67
drivers/s390/char/con3215.c
··· 102 102 static DEFINE_SPINLOCK(raw3215_freelist_lock); 103 103 104 104 static struct tty_driver *tty3215_driver; 105 + static bool con3215_drop = true; 105 106 106 107 /* 107 108 * Get a request structure from the free list ··· 448 447 } 449 448 450 449 /* 450 + * Need to drop data to avoid blocking. Drop as much data as possible. 451 + * This is unqueued part in the buffer and the queued part in the request. 452 + * Also adjust the head position to append new data and set count 453 + * accordingly. 454 + * 455 + * Return number of bytes available in buffer. 456 + */ 457 + static unsigned int raw3215_drop(struct raw3215_info *raw) 458 + { 459 + struct raw3215_req *req; 460 + 461 + req = raw->queued_write; 462 + if (req) { 463 + /* Drop queued data and delete request */ 464 + raw->written -= req->len; 465 + raw3215_free_req(req); 466 + raw->queued_write = NULL; 467 + } 468 + raw->head = (raw->head - raw->count + raw->written) & 469 + (RAW3215_BUFFER_SIZE - 1); 470 + raw->count = raw->written; 471 + 472 + return RAW3215_BUFFER_SIZE - raw->count; 473 + } 474 + 475 + /* 451 476 * Wait until length bytes are available int the output buffer. 477 + * If drop mode is active and wait condition holds true, start dropping 478 + * data. 452 479 * Has to be called with the s390irq lock held. Can be called 453 480 * disabled. 454 481 */ 455 - static void raw3215_make_room(struct raw3215_info *raw, unsigned int length) 482 + static unsigned int raw3215_make_room(struct raw3215_info *raw, 483 + unsigned int length, bool drop) 456 484 { 457 485 while (RAW3215_BUFFER_SIZE - raw->count < length) { 486 + if (drop) 487 + return raw3215_drop(raw); 488 + 458 489 /* there might be a request pending */ 459 490 raw->flags |= RAW3215_FLUSHING; 460 491 raw3215_mk_write_req(raw); ··· 503 470 udelay(100); 504 471 spin_lock(get_ccwdev_lock(raw->cdev)); 505 472 } 473 + return length; 474 + } 475 + 476 + #define RAW3215_COUNT 1 477 + #define RAW3215_STORE 2 478 + 479 + /* 480 + * Add text to console buffer. Find tabs in input and calculate size 481 + * including tab replacement. 482 + * This function operates in 2 different modes, depending on parameter 483 + * opmode: 484 + * RAW3215_COUNT: Get the size needed for the input string with 485 + * proper tab replacement calculation. 486 + * Return value is the number of bytes required to store the 487 + * input. However no data is actually stored. 488 + * The parameter todrop is not used. 489 + * RAW3215_STORE: Add data to the console buffer. The parameter todrop is 490 + * valid and contains the number of bytes to be dropped from head of 491 + * string without blocking. 492 + * Return value is the number of bytes copied. 493 + */ 494 + static unsigned int raw3215_addtext(const char *str, unsigned int length, 495 + struct raw3215_info *raw, int opmode, 496 + unsigned int todrop) 497 + { 498 + unsigned int c, ch, i, blanks, expanded_size = 0; 499 + unsigned int column = raw->line_pos; 500 + 501 + if (opmode == RAW3215_COUNT) 502 + todrop = 0; 503 + 504 + for (c = 0; c < length; ++c) { 505 + blanks = 1; 506 + ch = str[c]; 507 + 508 + switch (ch) { 509 + case '\n': 510 + expanded_size++; 511 + column = 0; 512 + break; 513 + case '\t': 514 + blanks = TAB_STOP_SIZE - (column % TAB_STOP_SIZE); 515 + column += blanks; 516 + expanded_size += blanks; 517 + ch = ' '; 518 + break; 519 + default: 520 + expanded_size++; 521 + column++; 522 + break; 523 + } 524 + 525 + if (opmode == RAW3215_COUNT) 526 + continue; 527 + if (todrop && expanded_size < todrop) /* Drop head data */ 528 + continue; 529 + for (i = 0; i < blanks; i++) { 530 + raw->buffer[raw->head] = (char)_ascebc[(int)ch]; 531 + raw->head = (raw->head + 1) & (RAW3215_BUFFER_SIZE - 1); 532 + raw->count++; 533 + } 534 + raw->line_pos = column; 535 + } 536 + return expanded_size - todrop; 506 537 } 507 538 508 539 /* ··· 575 478 static void raw3215_write(struct raw3215_info *raw, const char *str, 576 479 unsigned int length) 577 480 { 481 + unsigned int count, avail; 578 482 unsigned long flags; 579 - int c, count; 580 - 581 - while (length > 0) { 582 - spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); 583 - count = (length > RAW3215_BUFFER_SIZE) ? 584 - RAW3215_BUFFER_SIZE : length; 585 - length -= count; 586 - 587 - raw3215_make_room(raw, count); 588 - 589 - /* copy string to output buffer and convert it to EBCDIC */ 590 - while (1) { 591 - c = min_t(int, count, 592 - min(RAW3215_BUFFER_SIZE - raw->count, 593 - RAW3215_BUFFER_SIZE - raw->head)); 594 - if (c <= 0) 595 - break; 596 - memcpy(raw->buffer + raw->head, str, c); 597 - ASCEBC(raw->buffer + raw->head, c); 598 - raw->head = (raw->head + c) & (RAW3215_BUFFER_SIZE - 1); 599 - raw->count += c; 600 - raw->line_pos += c; 601 - str += c; 602 - count -= c; 603 - } 604 - if (!(raw->flags & RAW3215_WORKING)) { 605 - raw3215_mk_write_req(raw); 606 - /* start or queue request */ 607 - raw3215_try_io(raw); 608 - } 609 - spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); 610 - } 611 - } 612 - 613 - /* 614 - * Put character routine for 3215 devices 615 - */ 616 - static void raw3215_putchar(struct raw3215_info *raw, unsigned char ch) 617 - { 618 - unsigned long flags; 619 - unsigned int length, i; 620 483 621 484 spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); 622 - if (ch == '\t') { 623 - length = TAB_STOP_SIZE - (raw->line_pos%TAB_STOP_SIZE); 624 - raw->line_pos += length; 625 - ch = ' '; 626 - } else if (ch == '\n') { 627 - length = 1; 628 - raw->line_pos = 0; 629 - } else { 630 - length = 1; 631 - raw->line_pos++; 632 - } 633 - raw3215_make_room(raw, length); 634 485 635 - for (i = 0; i < length; i++) { 636 - raw->buffer[raw->head] = (char) _ascebc[(int) ch]; 637 - raw->head = (raw->head + 1) & (RAW3215_BUFFER_SIZE - 1); 638 - raw->count++; 486 + count = raw3215_addtext(str, length, raw, RAW3215_COUNT, 0); 487 + 488 + avail = raw3215_make_room(raw, count, con3215_drop); 489 + if (avail) { 490 + raw3215_addtext(str, length, raw, RAW3215_STORE, 491 + count - avail); 639 492 } 640 493 if (!(raw->flags & RAW3215_WORKING)) { 641 494 raw3215_mk_write_req(raw); ··· 593 546 raw3215_try_io(raw); 594 547 } 595 548 spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); 549 + } 550 + 551 + /* 552 + * Put character routine for 3215 devices 553 + */ 554 + static void raw3215_putchar(struct raw3215_info *raw, unsigned char ch) 555 + { 556 + raw3215_write(raw, &ch, 1); 596 557 } 597 558 598 559 /* ··· 778 723 { /* end of list */ }, 779 724 }; 780 725 726 + static ssize_t con_drop_store(struct device_driver *dev, const char *buf, size_t count) 727 + { 728 + bool drop; 729 + int rc; 730 + 731 + rc = kstrtobool(buf, &drop); 732 + if (!rc) 733 + con3215_drop = drop; 734 + return rc ?: count; 735 + } 736 + 737 + static ssize_t con_drop_show(struct device_driver *dev, char *buf) 738 + { 739 + return sysfs_emit(buf, "%d\n", con3215_drop ? 1 : 0); 740 + } 741 + 742 + static DRIVER_ATTR_RW(con_drop); 743 + 744 + static struct attribute *con3215_drv_attrs[] = { 745 + &driver_attr_con_drop.attr, 746 + NULL, 747 + }; 748 + 749 + static struct attribute_group con3215_drv_attr_group = { 750 + .attrs = con3215_drv_attrs, 751 + NULL, 752 + }; 753 + 754 + static const struct attribute_group *con3215_drv_attr_groups[] = { 755 + &con3215_drv_attr_group, 756 + NULL, 757 + }; 758 + 781 759 static struct ccw_driver raw3215_ccw_driver = { 782 760 .driver = { 783 761 .name = "3215", 762 + .groups = con3215_drv_attr_groups, 784 763 .owner = THIS_MODULE, 785 764 }, 786 765 .ids = raw3215_id, ··· 830 741 int i; 831 742 832 743 while (count > 0) { 833 - for (i = 0; i < count; i++) 834 - if (str[i] == '\t' || str[i] == '\n') 835 - break; 744 + i = min_t(int, count, RAW3215_BUFFER_SIZE - 1); 836 745 raw3215_write(raw, str, i); 837 746 count -= i; 838 747 str += i; 839 - if (count > 0) { 840 - raw3215_putchar(raw, *str); 841 - count--; 842 - str++; 843 - } 844 748 } 845 749 } 846 750 ··· 869 787 raw = raw3215[0]; /* console 3215 is the first one */ 870 788 if (!spin_trylock_irqsave(get_ccwdev_lock(raw->cdev), flags)) 871 789 return NOTIFY_DONE; 872 - raw3215_make_room(raw, RAW3215_BUFFER_SIZE); 790 + raw3215_make_room(raw, RAW3215_BUFFER_SIZE, false); 873 791 spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); 874 792 875 793 return NOTIFY_DONE; ··· 1130 1048 .stop = tty3215_stop, 1131 1049 .start = tty3215_start, 1132 1050 }; 1051 + 1052 + static int __init con3215_setup_drop(char *str) 1053 + { 1054 + bool drop; 1055 + int rc; 1056 + 1057 + rc = kstrtobool(str, &drop); 1058 + if (!rc) 1059 + con3215_drop = drop; 1060 + return rc; 1061 + } 1062 + early_param("con3215_drop", con3215_setup_drop); 1133 1063 1134 1064 /* 1135 1065 * 3215 tty registration code called from tty_init().