From 13f62e84385fa0241fc6a2178da50af02189121b Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 2 Nov 2022 15:16:43 +0100 Subject: [PATCH 001/182] s390/cmpxchg: use symbolic names for inline assembly operands Make cmpxchg() inline assemblies more readable by using symbolic names for operands. Link: https://lore.kernel.org/r/Y2J7yzQYt/bjLQXY@osiris Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 76 ++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 84c3f0d576c5..56fb8aa08945 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -96,56 +96,64 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, shift = (3 ^ (address & 3)) << 3; address ^= address & 3; asm volatile( - " l %0,%2\n" - "0: nr %0,%5\n" - " lr %1,%0\n" - " or %0,%3\n" - " or %1,%4\n" - " cs %0,%1,%2\n" - " jnl 1f\n" - " xr %1,%0\n" - " nr %1,%5\n" - " jnz 0b\n" + " l %[prev],%[address]\n" + "0: nr %[prev],%[mask]\n" + " lr %[tmp],%[prev]\n" + " or %[prev],%[old]\n" + " or %[tmp],%[new]\n" + " cs %[prev],%[tmp],%[address]\n" + " jnl 1f\n" + " xr %[tmp],%[prev]\n" + " nr %[tmp],%[mask]\n" + " jnz 0b\n" "1:" - : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) address) - : "d" ((old & 0xff) << shift), - "d" ((new & 0xff) << shift), - "d" (~(0xff << shift)) + : [prev] "=&d" (prev), + [tmp] "=&d" (tmp), + [address] "+Q" (*(int *)address) + : [old] "d" ((old & 0xff) << shift), + [new] "d" ((new & 0xff) << shift), + [mask] "d" (~(0xff << shift)) : "memory", "cc"); return prev >> shift; case 2: shift = (2 ^ (address & 2)) << 3; address ^= address & 2; asm volatile( - " l %0,%2\n" - "0: nr %0,%5\n" - " lr %1,%0\n" - " or %0,%3\n" - " or %1,%4\n" - " cs %0,%1,%2\n" - " jnl 1f\n" - " xr %1,%0\n" - " nr %1,%5\n" - " jnz 0b\n" + " l %[prev],%[address]\n" + "0: nr %[prev],%[mask]\n" + " lr %[tmp],%[prev]\n" + " or %[prev],%[old]\n" + " or %[tmp],%[new]\n" + " cs %[prev],%[tmp],%[address]\n" + " jnl 1f\n" + " xr %[tmp],%[prev]\n" + " nr %[tmp],%[mask]\n" + " jnz 0b\n" "1:" - : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) address) - : "d" ((old & 0xffff) << shift), - "d" ((new & 0xffff) << shift), - "d" (~(0xffff << shift)) + : [prev] "=&d" (prev), + [tmp] "=&d" (tmp), + [address] "+Q" (*(int *)address) + : [old] "d" ((old & 0xffff) << shift), + [new] "d" ((new & 0xffff) << shift), + [mask] "d" (~(0xffff << shift)) : "memory", "cc"); return prev >> shift; case 4: asm volatile( - " cs %0,%3,%1\n" - : "=&d" (prev), "+Q" (*(int *) address) - : "0" (old), "d" (new) + " cs %[prev],%[new],%[address]\n" + : [prev] "=&d" (prev), + [address] "+Q" (*(int *)address) + : "0" (old), + [new] "d" (new) : "memory", "cc"); return prev; case 8: asm volatile( - " csg %0,%3,%1\n" - : "=&d" (prev), "+QS" (*(long *) address) - : "0" (old), "d" (new) + " csg %[prev],%[new],%[address]\n" + : [prev] "=&d" (prev), + [address] "+QS" (*(long *)address) + : "0" (old), + [new] "d" (new) : "memory", "cc"); return prev; } From ce968f654570dbd9cac7de694681640061559d3b Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 2 Nov 2022 15:17:28 +0100 Subject: [PATCH 002/182] s390/cmpxchg: make variables local to each case label Make variables local to each case label. This limits the scope of variables and allows to use proper types everywhere. Link: https://lore.kernel.org/r/Y2J7+HqgAZwnfxsh@osiris Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 56fb8aa08945..02165acdaa93 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -88,11 +88,10 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, unsigned long old, unsigned long new, int size) { - unsigned long prev, tmp; - int shift; - switch (size) { - case 1: + case 1: { + unsigned int prev, tmp, shift; + shift = (3 ^ (address & 3)) << 3; address ^= address & 3; asm volatile( @@ -115,7 +114,10 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, [mask] "d" (~(0xff << shift)) : "memory", "cc"); return prev >> shift; - case 2: + } + case 2: { + unsigned int prev, tmp, shift; + shift = (2 ^ (address & 2)) << 3; address ^= address & 2; asm volatile( @@ -138,16 +140,22 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, [mask] "d" (~(0xffff << shift)) : "memory", "cc"); return prev >> shift; - case 4: + } + case 4: { + unsigned int prev; + asm volatile( " cs %[prev],%[new],%[address]\n" : [prev] "=&d" (prev), [address] "+Q" (*(int *)address) - : "0" (old), + : "0" ((unsigned int)old), [new] "d" (new) : "memory", "cc"); return prev; - case 8: + } + case 8: { + unsigned long prev; + asm volatile( " csg %[prev],%[new],%[address]\n" : [prev] "=&d" (prev), @@ -157,6 +165,7 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, : "memory", "cc"); return prev; } + } __cmpxchg_called_with_bad_pointer(); return old; } From e388d66f032166c8e8c4efd9e6c6f43610378903 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 2 Nov 2022 15:18:07 +0100 Subject: [PATCH 003/182] s390/cmpxchg: remove digits from input constraints Instead of using a digit for input constraints simply initialize the corresponding output operand in C code and use a "+" constraint modifier. Link: https://lore.kernel.org/r/Y2J8H82B6JhJhrp2@osiris Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 02165acdaa93..1c5785b851ec 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -142,26 +142,24 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, return prev >> shift; } case 4: { - unsigned int prev; + unsigned int prev = old; asm volatile( " cs %[prev],%[new],%[address]\n" - : [prev] "=&d" (prev), + : [prev] "+&d" (prev), [address] "+Q" (*(int *)address) - : "0" ((unsigned int)old), - [new] "d" (new) + : [new] "d" (new) : "memory", "cc"); return prev; } case 8: { - unsigned long prev; + unsigned long prev = old; asm volatile( " csg %[prev],%[new],%[address]\n" - : [prev] "=&d" (prev), + : [prev] "+&d" (prev), [address] "+QS" (*(long *)address) - : "0" (old), - [new] "d" (new) + : [new] "d" (new) : "memory", "cc"); return prev; } From f39a8c4a22ea104c368361b9ae0f550da161db2d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 2 Nov 2022 15:18:45 +0100 Subject: [PATCH 004/182] s390/extable: add EX_TABLE_UA_LOAD_REGPAIR() macro Add new exception table type which is able to handle register pairs. If an exception is recognized on such an instruction the specified register pair will be zeroed, and the specified error register will be modified so it contains -EFAULT, similar to the existing EX_TABLE_UA_LOAD_REG() macro. Link: https://lore.kernel.org/r/Y2J8RSW2khWLgpPo@osiris Signed-off-by: Heiko Carstens --- arch/s390/include/asm/asm-extable.h | 4 ++++ arch/s390/mm/extable.c | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/asm-extable.h b/arch/s390/include/asm/asm-extable.h index b74f1070ddb2..55a02a153dfc 100644 --- a/arch/s390/include/asm/asm-extable.h +++ b/arch/s390/include/asm/asm-extable.h @@ -12,6 +12,7 @@ #define EX_TYPE_UA_STORE 3 #define EX_TYPE_UA_LOAD_MEM 4 #define EX_TYPE_UA_LOAD_REG 5 +#define EX_TYPE_UA_LOAD_REGPAIR 6 #define EX_DATA_REG_ERR_SHIFT 0 #define EX_DATA_REG_ERR GENMASK(3, 0) @@ -85,4 +86,7 @@ #define EX_TABLE_UA_LOAD_REG(_fault, _target, _regerr, _regzero) \ __EX_TABLE_UA(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REG, _regerr, _regzero, 0) +#define EX_TABLE_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \ + __EX_TABLE_UA(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0) + #endif /* __ASM_EXTABLE_H */ diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 1e4d2187541a..fe87291df95d 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -47,13 +47,16 @@ static bool ex_handler_ua_load_mem(const struct exception_table_entry *ex, struc return true; } -static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, struct pt_regs *regs) +static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, + bool pair, struct pt_regs *regs) { unsigned int reg_zero = FIELD_GET(EX_DATA_REG_ADDR, ex->data); unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); regs->gprs[reg_err] = -EFAULT; regs->gprs[reg_zero] = 0; + if (pair) + regs->gprs[reg_zero + 1] = 0; regs->psw.addr = extable_fixup(ex); return true; } @@ -75,7 +78,9 @@ bool fixup_exception(struct pt_regs *regs) case EX_TYPE_UA_LOAD_MEM: return ex_handler_ua_load_mem(ex, regs); case EX_TYPE_UA_LOAD_REG: - return ex_handler_ua_load_reg(ex, regs); + return ex_handler_ua_load_reg(ex, false, regs); + case EX_TYPE_UA_LOAD_REGPAIR: + return ex_handler_ua_load_reg(ex, true, regs); } panic("invalid exception table entry"); } From 4148575abe1e14af3cb9fd1a3c9c2a708ec0b1f9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 2 Nov 2022 15:19:23 +0100 Subject: [PATCH 005/182] s390/uaccess: add cmpxchg_user_key() Add cmpxchg_user_key() which allows to execute a compare and exchange on a user space address. This allows also to specify a storage key which makes sure that key-controlled protection is considered. This is based on a patch written by Janis Schoetterl-Glausch. Link: https://lore.kernel.org/all/20220930210751.225873-2-scgl@linux.ibm.com Cc: Janis Schoetterl-Glausch Link: https://lore.kernel.org/r/Y2J8axs+bcQ2dO/l@osiris Signed-off-by: Heiko Carstens --- arch/s390/include/asm/uaccess.h | 183 ++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index f7038b800cc3..a125e60a1521 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -390,4 +390,187 @@ do { \ goto err_label; \ } while (0) +void __cmpxchg_user_key_called_with_bad_pointer(void); + +static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, + __uint128_t old, __uint128_t new, + unsigned long key, int size) +{ + int rc = 0; + + switch (size) { + case 1: { + unsigned int prev, tmp, shift; + + shift = (3 ^ (address & 3)) << 3; + address ^= address & 3; + asm volatile( + " spka 0(%[key])\n" + " sacf 256\n" + "0: l %[prev],%[address]\n" + "1: nr %[prev],%[mask]\n" + " lr %[tmp],%[prev]\n" + " or %[prev],%[old]\n" + " or %[tmp],%[new]\n" + "2: cs %[prev],%[tmp],%[address]\n" + "3: jnl 4f\n" + " xr %[tmp],%[prev]\n" + " nr %[tmp],%[mask]\n" + " jnz 1b\n" + "4: sacf 768\n" + " spka %[default_key]\n" + EX_TABLE_UA_LOAD_REG(0b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(2b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(3b, 4b, %[rc], %[prev]) + : [rc] "+&d" (rc), + [prev] "=&d" (prev), + [tmp] "=&d" (tmp), + [address] "+Q" (*(int *)address) + : [old] "d" (((unsigned int)old & 0xff) << shift), + [new] "d" (((unsigned int)new & 0xff) << shift), + [mask] "d" (~(0xff << shift)), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + *(unsigned char *)uval = prev >> shift; + return rc; + } + case 2: { + unsigned int prev, tmp, shift; + + shift = (2 ^ (address & 2)) << 3; + address ^= address & 2; + asm volatile( + " spka 0(%[key])\n" + " sacf 256\n" + "0: l %[prev],%[address]\n" + "1: nr %[prev],%[mask]\n" + " lr %[tmp],%[prev]\n" + " or %[prev],%[old]\n" + " or %[tmp],%[new]\n" + "2: cs %[prev],%[tmp],%[address]\n" + "3: jnl 4f\n" + " xr %[tmp],%[prev]\n" + " nr %[tmp],%[mask]\n" + " jnz 1b\n" + "4: sacf 768\n" + " spka %[default_key]\n" + EX_TABLE_UA_LOAD_REG(0b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(2b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(3b, 4b, %[rc], %[prev]) + : [rc] "+&d" (rc), + [prev] "=&d" (prev), + [tmp] "=&d" (tmp), + [address] "+Q" (*(int *)address) + : [old] "d" (((unsigned int)old & 0xffff) << shift), + [new] "d" (((unsigned int)new & 0xffff) << shift), + [mask] "d" (~(0xffff << shift)), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + *(unsigned short *)uval = prev >> shift; + return rc; + } + case 4: { + unsigned int prev = old; + + asm volatile( + " spka 0(%[key])\n" + " sacf 256\n" + "0: cs %[prev],%[new],%[address]\n" + "1: sacf 768\n" + " spka %[default_key]\n" + EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev]) + : [rc] "+&d" (rc), + [prev] "+&d" (prev), + [address] "+Q" (*(int *)address) + : [new] "d" ((unsigned int)new), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + *(unsigned int *)uval = prev; + return rc; + } + case 8: { + unsigned long prev = old; + + asm volatile( + " spka 0(%[key])\n" + " sacf 256\n" + "0: csg %[prev],%[new],%[address]\n" + "1: sacf 768\n" + " spka %[default_key]\n" + EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev]) + : [rc] "+&d" (rc), + [prev] "+&d" (prev), + [address] "+QS" (*(long *)address) + : [new] "d" ((unsigned long)new), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + *(unsigned long *)uval = prev; + return rc; + } + case 16: { + __uint128_t prev = old; + + asm volatile( + " spka 0(%[key])\n" + " sacf 256\n" + "0: cdsg %[prev],%[new],%[address]\n" + "1: sacf 768\n" + " spka %[default_key]\n" + EX_TABLE_UA_LOAD_REGPAIR(0b, 1b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REGPAIR(1b, 1b, %[rc], %[prev]) + : [rc] "+&d" (rc), + [prev] "+&d" (prev), + [address] "+QS" (*(__int128_t *)address) + : [new] "d" (new), + [key] "a" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY) + : "memory", "cc"); + *(__uint128_t *)uval = prev; + return rc; + } + } + __cmpxchg_user_key_called_with_bad_pointer(); + return rc; +} + +/** + * cmpxchg_user_key() - cmpxchg with user space target, honoring storage keys + * @ptr: User space address of value to compare to @old and exchange with + * @new. Must be aligned to sizeof(*@ptr). + * @uval: Address where the old value of *@ptr is written to. + * @old: Old value. Compared to the content pointed to by @ptr in order to + * determine if the exchange occurs. The old value read from *@ptr is + * written to *@uval. + * @new: New value to place at *@ptr. + * @key: Access key to use for checking storage key protection. + * + * Perform a cmpxchg on a user space target, honoring storage key protection. + * @key alone determines how key checking is performed, neither + * storage-protection-override nor fetch-protection-override apply. + * The caller must compare *@uval and @old to determine if values have been + * exchanged. In case of an exception *@uval is set to zero. + * + * Return: 0: cmpxchg executed + * -EFAULT: an exception happened when trying to access *@ptr + */ +#define cmpxchg_user_key(ptr, uval, old, new, key) \ +({ \ + __typeof__(ptr) __ptr = (ptr); \ + __typeof__(uval) __uval = (uval); \ + \ + BUILD_BUG_ON(sizeof(*(__ptr)) != sizeof(*(__uval))); \ + might_fault(); \ + __chk_user_ptr(__ptr); \ + __cmpxchg_user_key((unsigned long)(__ptr), (void *)(__uval), \ + (old), (new), (key), sizeof(*(__ptr))); \ +}) + #endif /* __S390_UACCESS_H */ From 51098f0eb22e2f54055d75dd25bc84eff07d6d8a Mon Sep 17 00:00:00 2001 From: Janis Schoetterl-Glausch Date: Wed, 16 Nov 2022 15:47:11 +0100 Subject: [PATCH 006/182] s390/cmpxchg: make loop condition for 1,2 byte cases precise The cmpxchg implementation for 1 and 2 bytes consists of a 4 byte cmpxchg loop. Currently, the decision to retry is imprecise, looping if bits outside the target byte(s) change instead of retrying until the target byte(s) differ from the old value. E.g. if an attempt to exchange (prev_left_0 old_bytes prev_right_0) is made and it fails because the word at the address is (prev_left_1 x prev_right_1) where both x != old_bytes and one of the prev_*_1 values differs from the respective prev_*_0 value, the cmpxchg is retried, even if by a semantic equivalent to a normal cmpxchg, the exchange would fail. Instead exit the loop if x != old_bytes and retry otherwise. Signed-off-by: Janis Schoetterl-Glausch Link: https://lore.kernel.org/r/20221116144711.3811011-1-scgl@linux.ibm.com Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cmpxchg.h | 60 ++++++++++++++----------- arch/s390/include/asm/uaccess.h | 80 ++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h index 1c5785b851ec..3f26416c2ad8 100644 --- a/arch/s390/include/asm/cmpxchg.h +++ b/arch/s390/include/asm/cmpxchg.h @@ -90,55 +90,63 @@ static __always_inline unsigned long __cmpxchg(unsigned long address, { switch (size) { case 1: { - unsigned int prev, tmp, shift; + unsigned int prev, shift, mask; shift = (3 ^ (address & 3)) << 3; address ^= address & 3; + old = (old & 0xff) << shift; + new = (new & 0xff) << shift; + mask = ~(0xff << shift); asm volatile( " l %[prev],%[address]\n" - "0: nr %[prev],%[mask]\n" - " lr %[tmp],%[prev]\n" - " or %[prev],%[old]\n" - " or %[tmp],%[new]\n" - " cs %[prev],%[tmp],%[address]\n" + " nr %[prev],%[mask]\n" + " xilf %[mask],0xffffffff\n" + " or %[new],%[prev]\n" + " or %[prev],%[tmp]\n" + "0: lr %[tmp],%[prev]\n" + " cs %[prev],%[new],%[address]\n" " jnl 1f\n" " xr %[tmp],%[prev]\n" + " xr %[new],%[tmp]\n" " nr %[tmp],%[mask]\n" - " jnz 0b\n" + " jz 0b\n" "1:" : [prev] "=&d" (prev), - [tmp] "=&d" (tmp), - [address] "+Q" (*(int *)address) - : [old] "d" ((old & 0xff) << shift), - [new] "d" ((new & 0xff) << shift), - [mask] "d" (~(0xff << shift)) - : "memory", "cc"); + [address] "+Q" (*(int *)address), + [tmp] "+&d" (old), + [new] "+&d" (new), + [mask] "+&d" (mask) + :: "memory", "cc"); return prev >> shift; } case 2: { - unsigned int prev, tmp, shift; + unsigned int prev, shift, mask; shift = (2 ^ (address & 2)) << 3; address ^= address & 2; + old = (old & 0xffff) << shift; + new = (new & 0xffff) << shift; + mask = ~(0xffff << shift); asm volatile( " l %[prev],%[address]\n" - "0: nr %[prev],%[mask]\n" - " lr %[tmp],%[prev]\n" - " or %[prev],%[old]\n" - " or %[tmp],%[new]\n" - " cs %[prev],%[tmp],%[address]\n" + " nr %[prev],%[mask]\n" + " xilf %[mask],0xffffffff\n" + " or %[new],%[prev]\n" + " or %[prev],%[tmp]\n" + "0: lr %[tmp],%[prev]\n" + " cs %[prev],%[new],%[address]\n" " jnl 1f\n" " xr %[tmp],%[prev]\n" + " xr %[new],%[tmp]\n" " nr %[tmp],%[mask]\n" - " jnz 0b\n" + " jz 0b\n" "1:" : [prev] "=&d" (prev), - [tmp] "=&d" (tmp), - [address] "+Q" (*(int *)address) - : [old] "d" ((old & 0xffff) << shift), - [new] "d" ((new & 0xffff) << shift), - [mask] "d" (~(0xffff << shift)) - : "memory", "cc"); + [address] "+Q" (*(int *)address), + [tmp] "+&d" (old), + [new] "+&d" (new), + [mask] "+&d" (mask) + :: "memory", "cc"); return prev >> shift; } case 4: { diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index a125e60a1521..d028ee59e941 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -400,74 +400,82 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, switch (size) { case 1: { - unsigned int prev, tmp, shift; + unsigned int prev, shift, mask, _old, _new; shift = (3 ^ (address & 3)) << 3; address ^= address & 3; + _old = (old & 0xff) << shift; + _new = (new & 0xff) << shift; + mask = ~(0xff << shift); asm volatile( " spka 0(%[key])\n" " sacf 256\n" "0: l %[prev],%[address]\n" "1: nr %[prev],%[mask]\n" - " lr %[tmp],%[prev]\n" - " or %[prev],%[old]\n" - " or %[tmp],%[new]\n" - "2: cs %[prev],%[tmp],%[address]\n" - "3: jnl 4f\n" + " xilf %[mask],0xffffffff\n" + " or %[new],%[prev]\n" + " or %[prev],%[tmp]\n" + "2: lr %[tmp],%[prev]\n" + "3: cs %[prev],%[new],%[address]\n" + "4: jnl 5f\n" " xr %[tmp],%[prev]\n" + " xr %[new],%[tmp]\n" " nr %[tmp],%[mask]\n" - " jnz 1b\n" - "4: sacf 768\n" + " jz 2b\n" + "5: sacf 768\n" " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REG(0b, 4b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(1b, 4b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(2b, 4b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(3b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev]) : [rc] "+&d" (rc), [prev] "=&d" (prev), - [tmp] "=&d" (tmp), - [address] "+Q" (*(int *)address) - : [old] "d" (((unsigned int)old & 0xff) << shift), - [new] "d" (((unsigned int)new & 0xff) << shift), - [mask] "d" (~(0xff << shift)), - [key] "a" (key << 4), + [address] "+Q" (*(int *)address), + [tmp] "+&d" (_old), + [new] "+&d" (_new), + [mask] "+&d" (mask) + : [key] "a" (key << 4), [default_key] "J" (PAGE_DEFAULT_KEY) : "memory", "cc"); *(unsigned char *)uval = prev >> shift; return rc; } case 2: { - unsigned int prev, tmp, shift; + unsigned int prev, shift, mask, _old, _new; shift = (2 ^ (address & 2)) << 3; address ^= address & 2; + _old = (old & 0xffff) << shift; + _new = (new & 0xffff) << shift; + mask = ~(0xffff << shift); asm volatile( " spka 0(%[key])\n" " sacf 256\n" "0: l %[prev],%[address]\n" "1: nr %[prev],%[mask]\n" - " lr %[tmp],%[prev]\n" - " or %[prev],%[old]\n" - " or %[tmp],%[new]\n" - "2: cs %[prev],%[tmp],%[address]\n" - "3: jnl 4f\n" + " xilf %[mask],0xffffffff\n" + " or %[new],%[prev]\n" + " or %[prev],%[tmp]\n" + "2: lr %[tmp],%[prev]\n" + "3: cs %[prev],%[new],%[address]\n" + "4: jnl 5f\n" " xr %[tmp],%[prev]\n" + " xr %[new],%[tmp]\n" " nr %[tmp],%[mask]\n" - " jnz 1b\n" - "4: sacf 768\n" + " jz 2b\n" + "5: sacf 768\n" " spka %[default_key]\n" - EX_TABLE_UA_LOAD_REG(0b, 4b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(1b, 4b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(2b, 4b, %[rc], %[prev]) - EX_TABLE_UA_LOAD_REG(3b, 4b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev]) + EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev]) : [rc] "+&d" (rc), [prev] "=&d" (prev), - [tmp] "=&d" (tmp), - [address] "+Q" (*(int *)address) - : [old] "d" (((unsigned int)old & 0xffff) << shift), - [new] "d" (((unsigned int)new & 0xffff) << shift), - [mask] "d" (~(0xffff << shift)), - [key] "a" (key << 4), + [address] "+Q" (*(int *)address), + [tmp] "+&d" (_old), + [new] "+&d" (_new), + [mask] "+&d" (mask) + : [key] "a" (key << 4), [default_key] "J" (PAGE_DEFAULT_KEY) : "memory", "cc"); *(unsigned short *)uval = prev >> shift; From 739ad2e4e15b585a0eaf98b7bdee62b2dd9588c9 Mon Sep 17 00:00:00 2001 From: Janis Schoetterl-Glausch Date: Thu, 17 Nov 2022 11:07:45 +0100 Subject: [PATCH 007/182] s390/uaccess: limit number of retries for cmpxchg_user_key() cmpxchg_user_key() for byte and short values is implemented via a one word cmpxchg loop. Give up trying to perform the cmpxchg if it fails too often because of contention on the cache line. This ensures that the thread cannot become stuck in the kernel. Signed-off-by: Janis Schoetterl-Glausch Link: https://lore.kernel.org/r/20221117100745.3253896-1-scgl@linux.ibm.com Signed-off-by: Heiko Carstens --- arch/s390/include/asm/uaccess.h | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index d028ee59e941..7c10f594e747 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -392,6 +392,8 @@ do { \ void __cmpxchg_user_key_called_with_bad_pointer(void); +#define CMPXCHG_USER_KEY_MAX_LOOPS 128 + static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, __uint128_t old, __uint128_t new, unsigned long key, int size) @@ -401,6 +403,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, switch (size) { case 1: { unsigned int prev, shift, mask, _old, _new; + unsigned long count; shift = (3 ^ (address & 3)) << 3; address ^= address & 3; @@ -410,6 +413,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, asm volatile( " spka 0(%[key])\n" " sacf 256\n" + " llill %[count],%[max_loops]\n" "0: l %[prev],%[address]\n" "1: nr %[prev],%[mask]\n" " xilf %[mask],0xffffffff\n" @@ -421,7 +425,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, " xr %[tmp],%[prev]\n" " xr %[new],%[tmp]\n" " nr %[tmp],%[mask]\n" - " jz 2b\n" + " jnz 5f\n" + " brct %[count],2b\n" "5: sacf 768\n" " spka %[default_key]\n" EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) @@ -433,15 +438,20 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, [address] "+Q" (*(int *)address), [tmp] "+&d" (_old), [new] "+&d" (_new), - [mask] "+&d" (mask) - : [key] "a" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY) + [mask] "+&d" (mask), + [count] "=a" (count) + : [key] "%[count]" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY), + [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS) : "memory", "cc"); *(unsigned char *)uval = prev >> shift; + if (!count) + rc = -EAGAIN; return rc; } case 2: { unsigned int prev, shift, mask, _old, _new; + unsigned long count; shift = (2 ^ (address & 2)) << 3; address ^= address & 2; @@ -451,6 +461,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, asm volatile( " spka 0(%[key])\n" " sacf 256\n" + " llill %[count],%[max_loops]\n" "0: l %[prev],%[address]\n" "1: nr %[prev],%[mask]\n" " xilf %[mask],0xffffffff\n" @@ -462,7 +473,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, " xr %[tmp],%[prev]\n" " xr %[new],%[tmp]\n" " nr %[tmp],%[mask]\n" - " jz 2b\n" + " jnz 5f\n" + " brct %[count],2b\n" "5: sacf 768\n" " spka %[default_key]\n" EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev]) @@ -474,11 +486,15 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, [address] "+Q" (*(int *)address), [tmp] "+&d" (_old), [new] "+&d" (_new), - [mask] "+&d" (mask) - : [key] "a" (key << 4), - [default_key] "J" (PAGE_DEFAULT_KEY) + [mask] "+&d" (mask), + [count] "=a" (count) + : [key] "%[count]" (key << 4), + [default_key] "J" (PAGE_DEFAULT_KEY), + [max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS) : "memory", "cc"); *(unsigned short *)uval = prev >> shift; + if (!count) + rc = -EAGAIN; return rc; } case 4: { @@ -568,6 +584,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, * * Return: 0: cmpxchg executed * -EFAULT: an exception happened when trying to access *@ptr + * -EAGAIN: maxed out number of retries (byte and short only) */ #define cmpxchg_user_key(ptr, uval, old, new, key) \ ({ \ From b33d59fb37ddcb6ee65d4fa23cc3d58793d13c5b Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 4 Jan 2023 16:55:53 +0100 Subject: [PATCH 008/182] s390/uaccess: avoid __ashlti3() call __cmpxchg_user_key() uses 128 bit types which, depending on compiler and config options, may lead to an __ashlti3() library call. Get rid of that by simply casting the 128 bit values to 32 bit values. Reported-by: kernel test robot Suggested-by: Janis Schoetterl-Glausch Fixes: 51098f0eb22e ("s390/cmpxchg: make loop condition for 1,2 byte cases precise") Link: https://lore.kernel.org/all/4b96b112d5415d08a81d30657feec2c8c3000f7c.camel@linux.ibm.com/ Signed-off-by: Heiko Carstens --- arch/s390/include/asm/uaccess.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index 7c10f594e747..8a8c64a678c4 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -407,8 +407,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, shift = (3 ^ (address & 3)) << 3; address ^= address & 3; - _old = (old & 0xff) << shift; - _new = (new & 0xff) << shift; + _old = ((unsigned int)old & 0xff) << shift; + _new = ((unsigned int)new & 0xff) << shift; mask = ~(0xff << shift); asm volatile( " spka 0(%[key])\n" @@ -455,8 +455,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval, shift = (2 ^ (address & 2)) << 3; address ^= address & 2; - _old = (old & 0xffff) << shift; - _new = (new & 0xffff) << shift; + _old = ((unsigned int)old & 0xffff) << shift; + _new = ((unsigned int)new & 0xffff) << shift; mask = ~(0xffff << shift); asm volatile( " spka 0(%[key])\n" From 91d5364dc673fa9cf3a5b7b30cf33c70803eb3a4 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 23 Dec 2022 11:03:32 +0100 Subject: [PATCH 009/182] s390/cpumf: support user space events for counting CPU Measurement counting facility events PROBLEM_STATE_CPU_CYCLES(32) and PROBLEM_STATE_INSTRUCTIONS(33) are valid events. However the device driver returns error -EOPNOTSUPP when these event are to be installed. Fix this and allow installation of events PROBLEM_STATE_CPU_CYCLES, PROBLEM_STATE_CPU_CYCLES:u, PROBLEM_STATE_INSTRUCTIONS and PROBLEM_STATE_INSTRUCTIONS:u. Kernel space counting only is still not supported by s390. Signed-off-by: Thomas Richter Acked-by: Sumanth Korikkar Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_cf.c | 35 ++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index f043a7ff220b..28fa80fd69fa 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -2,7 +2,7 @@ /* * Performance event support for s390x - CPU-measurement Counter Facility * - * Copyright IBM Corp. 2012, 2021 + * Copyright IBM Corp. 2012, 2022 * Author(s): Hendrik Brueckner * Thomas Richter */ @@ -434,6 +434,12 @@ static void cpumf_hw_inuse(void) mutex_unlock(&pmc_reserve_mutex); } +static int is_userspace_event(u64 ev) +{ + return cpumf_generic_events_user[PERF_COUNT_HW_CPU_CYCLES] == ev || + cpumf_generic_events_user[PERF_COUNT_HW_INSTRUCTIONS] == ev; +} + static int __hw_perf_event_init(struct perf_event *event, unsigned int type) { struct perf_event_attr *attr = &event->attr; @@ -456,19 +462,26 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (is_sampling_event(event)) /* No sampling support */ return -ENOENT; ev = attr->config; - /* Count user space (problem-state) only */ if (!attr->exclude_user && attr->exclude_kernel) { - if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) - return -EOPNOTSUPP; - ev = cpumf_generic_events_user[ev]; - - /* No support for kernel space counters only */ + /* + * Count user space (problem-state) only + * Handle events 32 and 33 as 0:u and 1:u + */ + if (!is_userspace_event(ev)) { + if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) + return -EOPNOTSUPP; + ev = cpumf_generic_events_user[ev]; + } } else if (!attr->exclude_kernel && attr->exclude_user) { + /* No support for kernel space counters only */ return -EOPNOTSUPP; - } else { /* Count user and kernel space */ - if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) - return -EOPNOTSUPP; - ev = cpumf_generic_events_basic[ev]; + } else { + /* Count user and kernel space, incl. events 32 + 33 */ + if (!is_userspace_event(ev)) { + if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) + return -EOPNOTSUPP; + ev = cpumf_generic_events_basic[ev]; + } } break; From df386f15b2fbe51863d0f076d5939d5cbdb36f0d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 6 Oct 2022 20:56:35 +1000 Subject: [PATCH 010/182] s390: remove the last remnants of cputime_t cputime_t was a core kernel type, removed by commits ed5c8c854f2b..b672592f0221. As explained in commit b672592f0221 ("sched/cputime: Remove generic asm headers"), the final cleanup is for the arch to provide cputime_to_nsec[s](). Commit e53051e757d6 ("s390/cputime: provide archicture specific cputime_to_nsecs") did that, but just didn't remove the then-unused cputime_to_usecs() and associated remnants. Signed-off-by: Nicholas Piggin Reviewed-by: Sven Schnelle Signed-off-by: Alexander Gordeev Link: https://lore.kernel.org/r/20221006105635.115775-1-npiggin@gmail.com Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cputime.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h index 1d389847b588..7f9284e2a7db 100644 --- a/arch/s390/include/asm/cputime.h +++ b/arch/s390/include/asm/cputime.h @@ -11,21 +11,6 @@ #include #include -#define CPUTIME_PER_USEC 4096ULL -#define CPUTIME_PER_SEC (CPUTIME_PER_USEC * USEC_PER_SEC) - -/* We want to use full resolution of the CPU timer: 2**-12 micro-seconds. */ - -#define cmpxchg_cputime(ptr, old, new) cmpxchg64(ptr, old, new) - -/* - * Convert cputime to microseconds. - */ -static inline u64 cputime_to_usecs(const u64 cputime) -{ - return cputime >> 12; -} - /* * Convert cputime to nanoseconds. */ From a21e962e129db40ae08af9fc80ea7babf3632d77 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:24:15 +0100 Subject: [PATCH 011/182] s390/tty3270: add tty3270_create_view() Will be used later for the console driver. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/tty3270.c | 57 +++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 26e3995ac062..3ea5df58b64b 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -953,27 +953,14 @@ static struct raw3270_fn tty3270_fn = { .resize = tty3270_resize }; -/* - * This routine is called whenever a 3270 tty is opened first time. - */ -static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) +static int +tty3270_create_view(int index, struct tty3270 **newtp) { - struct raw3270_view *view; struct tty3270 *tp; int i, rc; - /* Check if the tty3270 is already there. */ - view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); - if (!IS_ERR(view)) { - tp = container_of(view, struct tty3270, view); - tty->driver_data = tp; - tty->winsize.ws_row = tp->view.rows - 2; - tty->winsize.ws_col = tp->view.cols; - tp->inattr = TF_INPUT; - goto port_install; - } - if (tty3270_max_index < tty->index + 1) - tty3270_max_index = tty->index + 1; + if (tty3270_max_index < index + 1) + tty3270_max_index = index + 1; /* Allocate tty3270 structure on first open. */ tp = tty3270_alloc_view(); @@ -981,7 +968,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return PTR_ERR(tp); rc = raw3270_add_view(&tp->view, &tty3270_fn, - tty->index + RAW3270_FIRSTMINOR, + index + RAW3270_FIRSTMINOR, RAW3270_VIEW_LOCK_BH); if (rc) { tty3270_free_view(tp); @@ -997,9 +984,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return rc; } - tty->winsize.ws_row = tp->view.rows - 2; - tty->winsize.ws_col = tp->view.cols; - tty3270_create_prompt(tp); tty3270_create_status(tp); tty3270_update_status(tp); @@ -1016,16 +1000,41 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) kbd_ascebc(tp->kbd, tp->view.ascebc); raw3270_activate_view(&tp->view); + raw3270_put_view(&tp->view); + *newtp = tp; + return 0; +} -port_install: +/* + * This routine is called whenever a 3270 tty is opened first time. + */ +static int +tty3270_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct raw3270_view *view; + struct tty3270 *tp; + int rc; + + /* Check if the tty3270 is already there. */ + view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); + if (IS_ERR(view)) { + rc = tty3270_create_view(tty->index, &tp); + if (rc) + return rc; + } else { + tp = container_of(view, struct tty3270, view); + tty->driver_data = tp; + tp->inattr = TF_INPUT; + } + + tty->winsize.ws_row = tp->view.rows - 2; + tty->winsize.ws_col = tp->view.cols; rc = tty_port_install(&tp->port, driver, tty); if (rc) { raw3270_put_view(&tp->view); return rc; } - tty->driver_data = tp; - return 0; } From c17fe081ac1f397feaed6c8a279ebb3f647f359a Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:22:28 +0100 Subject: [PATCH 012/182] s390/3270: unify con3270 + tty3270 There were two flavours of 3270 drivers: con3270, which provides a console driver, and tty3270 providing a tty driver. A user could switch with PF3 between the two views. While this sounds nice, it's a bit annoying: If the user enters a command which triggers a kernel message, he always has to switch back and forth to see the message. Unify both views to have kernel messages and tty in one screen, like it is on almost all other platforms. This also has the nice side effect of removing a lot of duplicate code. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/Makefile | 1 - drivers/s390/char/con3270.c | 651 ------------------------------------ drivers/s390/char/tty3270.c | 181 ++++++++-- drivers/s390/char/tty3270.h | 15 - 4 files changed, 158 insertions(+), 690 deletions(-) delete mode 100644 drivers/s390/char/con3270.c delete mode 100644 drivers/s390/char/tty3270.h diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index ce32270082f5..e3bcbbb98b9f 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -22,7 +22,6 @@ obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ sclp_early.o sclp_early_core.o sclp_sd.o obj-$(CONFIG_TN3270) += raw3270.o -obj-$(CONFIG_TN3270_CONSOLE) += con3270.o obj-$(CONFIG_TN3270_TTY) += tty3270.o obj-$(CONFIG_TN3270_FS) += fs3270.o diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c deleted file mode 100644 index 10f6a37fb153..000000000000 --- a/drivers/s390/char/con3270.c +++ /dev/null @@ -1,651 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * IBM/3270 Driver - console view. - * - * Author(s): - * Original 3270 Code for 2.4 written by Richard Hitt (UTS Global) - * Rewritten for 2.5 by Martin Schwidefsky - * Copyright IBM Corp. 2003, 2009 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "raw3270.h" -#include "tty3270.h" -#include "ctrlchar.h" - -#define CON3270_OUTPUT_BUFFER_SIZE 1024 -#define CON3270_STRING_PAGES 4 - -static struct raw3270_fn con3270_fn; - -static bool auto_update = true; -module_param(auto_update, bool, 0); - -/* - * Main 3270 console view data structure. - */ -struct con3270 { - struct raw3270_view view; - struct list_head freemem; /* list of free memory for strings. */ - - /* Output stuff. */ - struct list_head lines; /* list of lines. */ - struct list_head update; /* list of lines to update. */ - int line_nr; /* line number for next update. */ - int nr_lines; /* # lines in list. */ - int nr_up; /* # lines up in history. */ - unsigned long update_flags; /* Update indication bits. */ - struct string *cline; /* current output line. */ - struct string *status; /* last line of display. */ - struct raw3270_request *write; /* single write request. */ - struct timer_list timer; - - /* Input stuff. */ - struct string *input; /* input string for read request. */ - struct raw3270_request *read; /* single read request. */ - struct raw3270_request *kreset; /* single keyboard reset request. */ - struct tasklet_struct readlet; /* tasklet to issue read request. */ -}; - -static struct con3270 *condev; - -/* con3270->update_flags. See con3270_update for details. */ -#define CON_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ -#define CON_UPDATE_LIST 2 /* Update lines in tty3270->update. */ -#define CON_UPDATE_STATUS 4 /* Update status line. */ -#define CON_UPDATE_ALL 8 /* Recreate screen. */ - -static void con3270_update(struct timer_list *); - -/* - * Setup timeout for a device. On timeout trigger an update. - */ -static void con3270_set_timer(struct con3270 *cp, int expires) -{ - if (expires == 0) - del_timer(&cp->timer); - else - mod_timer(&cp->timer, jiffies + expires); -} - -/* - * The status line is the last line of the screen. It shows the string - * "console view" in the lower left corner and "Running"/"More..."/"Holding" - * in the lower right corner of the screen. - */ -static void -con3270_update_status(struct con3270 *cp) -{ - char *str; - - str = (cp->nr_up != 0) ? "History" : "Running"; - memcpy(cp->status->string + 24, str, 7); - codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); - cp->update_flags |= CON_UPDATE_STATUS; -} - -static void -con3270_create_status(struct con3270 *cp) -{ - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, TO_SF,TF_LOG,TO_SA,TAT_COLOR, TAC_GREEN, - 'c','o','n','s','o','l','e',' ','v','i','e','w', - TO_RA,0,0,0,'R','u','n','n','i','n','g',TO_SF,TF_LOG }; - - cp->status = alloc_string(&cp->freemem, sizeof(blueprint)); - /* Copy blueprint to status line */ - memcpy(cp->status->string, blueprint, sizeof(blueprint)); - /* Set TO_RA addresses. */ - raw3270_buffer_address(cp->view.dev, cp->status->string + 1, - cp->view.cols * (cp->view.rows - 1)); - raw3270_buffer_address(cp->view.dev, cp->status->string + 21, - cp->view.cols * cp->view.rows - 8); - /* Convert strings to ebcdic. */ - codepage_convert(cp->view.ascebc, cp->status->string + 8, 12); - codepage_convert(cp->view.ascebc, cp->status->string + 24, 7); -} - -/* - * Set output offsets to 3270 datastream fragment of a console string. - */ -static void -con3270_update_string(struct con3270 *cp, struct string *s, int nr) -{ - if (s->len < 4) { - /* This indicates a bug, but printing a warning would - * cause a deadlock. */ - return; - } - if (s->string[s->len - 4] != TO_RA) - return; - raw3270_buffer_address(cp->view.dev, s->string + s->len - 3, - cp->view.cols * (nr + 1)); -} - -/* - * Rebuild update list to print all lines. - */ -static void -con3270_rebuild_update(struct con3270 *cp) -{ - struct string *s, *n; - int nr; - - /* - * Throw away update list and create a new one, - * containing all lines that will fit on the screen. - */ - list_for_each_entry_safe(s, n, &cp->update, update) - list_del_init(&s->update); - nr = cp->view.rows - 2 + cp->nr_up; - list_for_each_entry_reverse(s, &cp->lines, list) { - if (nr < cp->view.rows - 1) - list_add(&s->update, &cp->update); - if (--nr < 0) - break; - } - cp->line_nr = 0; - cp->update_flags |= CON_UPDATE_LIST; -} - -/* - * Alloc string for size bytes. Free strings from history if necessary. - */ -static struct string * -con3270_alloc_string(struct con3270 *cp, size_t size) -{ - struct string *s, *n; - - s = alloc_string(&cp->freemem, size); - if (s) - return s; - list_for_each_entry_safe(s, n, &cp->lines, list) { - list_del(&s->list); - if (!list_empty(&s->update)) - list_del(&s->update); - cp->nr_lines--; - if (free_string(&cp->freemem, s) >= size) - break; - } - s = alloc_string(&cp->freemem, size); - BUG_ON(!s); - if (cp->nr_up != 0 && cp->nr_up + cp->view.rows > cp->nr_lines) { - cp->nr_up = cp->nr_lines - cp->view.rows + 1; - con3270_rebuild_update(cp); - con3270_update_status(cp); - } - return s; -} - -/* - * Write completion callback. - */ -static void -con3270_write_callback(struct raw3270_request *rq, void *data) -{ - raw3270_request_reset(rq); - xchg(&((struct con3270 *) rq->view)->write, rq); -} - -/* - * Update console display. - */ -static void -con3270_update(struct timer_list *t) -{ - struct con3270 *cp = from_timer(cp, t, timer); - struct raw3270_request *wrq; - char wcc, prolog[6]; - unsigned long flags; - unsigned long updated; - struct string *s, *n; - int rc; - - if (!auto_update && !raw3270_view_active(&cp->view)) - return; - if (cp->view.dev) - raw3270_activate_view(&cp->view); - - wrq = xchg(&cp->write, 0); - if (!wrq) { - con3270_set_timer(cp, 1); - return; - } - - spin_lock_irqsave(&cp->view.lock, flags); - updated = 0; - if (cp->update_flags & CON_UPDATE_ALL) { - con3270_rebuild_update(cp); - con3270_update_status(cp); - cp->update_flags = CON_UPDATE_ERASE | CON_UPDATE_LIST | - CON_UPDATE_STATUS; - } - if (cp->update_flags & CON_UPDATE_ERASE) { - /* Use erase write alternate to initialize display. */ - raw3270_request_set_cmd(wrq, TC_EWRITEA); - updated |= CON_UPDATE_ERASE; - } else - raw3270_request_set_cmd(wrq, TC_WRITE); - - wcc = TW_NONE; - raw3270_request_add_data(wrq, &wcc, 1); - - /* - * Update status line. - */ - if (cp->update_flags & CON_UPDATE_STATUS) - if (raw3270_request_add_data(wrq, cp->status->string, - cp->status->len) == 0) - updated |= CON_UPDATE_STATUS; - - if (cp->update_flags & CON_UPDATE_LIST) { - prolog[0] = TO_SBA; - prolog[3] = TO_SA; - prolog[4] = TAT_COLOR; - prolog[5] = TAC_TURQ; - raw3270_buffer_address(cp->view.dev, prolog + 1, - cp->view.cols * cp->line_nr); - raw3270_request_add_data(wrq, prolog, 6); - /* Write strings in the update list to the screen. */ - list_for_each_entry_safe(s, n, &cp->update, update) { - if (s != cp->cline) - con3270_update_string(cp, s, cp->line_nr); - if (raw3270_request_add_data(wrq, s->string, - s->len) != 0) - break; - list_del_init(&s->update); - if (s != cp->cline) - cp->line_nr++; - } - if (list_empty(&cp->update)) - updated |= CON_UPDATE_LIST; - } - wrq->callback = con3270_write_callback; - rc = raw3270_start(&cp->view, wrq); - if (rc == 0) { - cp->update_flags &= ~updated; - if (cp->update_flags) - con3270_set_timer(cp, 1); - } else { - raw3270_request_reset(wrq); - xchg(&cp->write, wrq); - } - spin_unlock_irqrestore(&cp->view.lock, flags); -} - -/* - * Read tasklet. - */ -static void -con3270_read_tasklet(unsigned long data) -{ - static char kreset_data = TW_KR; - struct raw3270_request *rrq; - struct con3270 *cp; - unsigned long flags; - int nr_up, deactivate; - - rrq = (struct raw3270_request *)data; - cp = (struct con3270 *) rrq->view; - spin_lock_irqsave(&cp->view.lock, flags); - nr_up = cp->nr_up; - deactivate = 0; - /* Check aid byte. */ - switch (cp->input->string[0]) { - case 0x7d: /* enter: jump to bottom. */ - nr_up = 0; - break; - case 0xf3: /* PF3: deactivate the console view. */ - deactivate = 1; - break; - case 0x6d: /* clear: start from scratch. */ - cp->update_flags = CON_UPDATE_ALL; - con3270_set_timer(cp, 1); - break; - case 0xf7: /* PF7: do a page up in the console log. */ - nr_up += cp->view.rows - 2; - if (nr_up + cp->view.rows - 1 > cp->nr_lines) { - nr_up = cp->nr_lines - cp->view.rows + 1; - if (nr_up < 0) - nr_up = 0; - } - break; - case 0xf8: /* PF8: do a page down in the console log. */ - nr_up -= cp->view.rows - 2; - if (nr_up < 0) - nr_up = 0; - break; - } - if (nr_up != cp->nr_up) { - cp->nr_up = nr_up; - con3270_rebuild_update(cp); - con3270_update_status(cp); - con3270_set_timer(cp, 1); - } - spin_unlock_irqrestore(&cp->view.lock, flags); - - /* Start keyboard reset command. */ - raw3270_request_reset(cp->kreset); - raw3270_request_set_cmd(cp->kreset, TC_WRITE); - raw3270_request_add_data(cp->kreset, &kreset_data, 1); - raw3270_start(&cp->view, cp->kreset); - - if (deactivate) - raw3270_deactivate_view(&cp->view); - - raw3270_request_reset(rrq); - xchg(&cp->read, rrq); - raw3270_put_view(&cp->view); -} - -/* - * Read request completion callback. - */ -static void -con3270_read_callback(struct raw3270_request *rq, void *data) -{ - raw3270_get_view(rq->view); - /* Schedule tasklet to pass input to tty. */ - tasklet_schedule(&((struct con3270 *) rq->view)->readlet); -} - -/* - * Issue a read request. Called only from interrupt function. - */ -static void -con3270_issue_read(struct con3270 *cp) -{ - struct raw3270_request *rrq; - int rc; - - rrq = xchg(&cp->read, 0); - if (!rrq) - /* Read already scheduled. */ - return; - rrq->callback = con3270_read_callback; - rrq->callback_data = cp; - raw3270_request_set_cmd(rrq, TC_READMOD); - raw3270_request_set_data(rrq, cp->input->string, cp->input->len); - /* Issue the read modified request. */ - rc = raw3270_start_irq(&cp->view, rrq); - if (rc) - raw3270_request_reset(rrq); -} - -/* - * Switch to the console view. - */ -static int -con3270_activate(struct raw3270_view *view) -{ - struct con3270 *cp; - - cp = (struct con3270 *) view; - cp->update_flags = CON_UPDATE_ALL; - con3270_set_timer(cp, 1); - return 0; -} - -static void -con3270_deactivate(struct raw3270_view *view) -{ - struct con3270 *cp; - - cp = (struct con3270 *) view; - del_timer(&cp->timer); -} - -static void -con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) -{ - /* Handle ATTN. Schedule tasklet to read aid. */ - if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) - con3270_issue_read(cp); - - if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) - rq->rc = -EIO; - else - /* Normal end. Copy residual count. */ - rq->rescnt = irb->scsw.cmd.count; - } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { - /* Interrupt without an outstanding request -> update all */ - cp->update_flags = CON_UPDATE_ALL; - con3270_set_timer(cp, 1); - } -} - -/* Console view to a 3270 device. */ -static struct raw3270_fn con3270_fn = { - .activate = con3270_activate, - .deactivate = con3270_deactivate, - .intv = (void *) con3270_irq -}; - -static inline void -con3270_cline_add(struct con3270 *cp) -{ - if (!list_empty(&cp->cline->list)) - /* Already added. */ - return; - list_add_tail(&cp->cline->list, &cp->lines); - cp->nr_lines++; - con3270_rebuild_update(cp); -} - -static inline void -con3270_cline_insert(struct con3270 *cp, unsigned char c) -{ - cp->cline->string[cp->cline->len++] = - cp->view.ascebc[(c < ' ') ? ' ' : c]; - if (list_empty(&cp->cline->update)) { - list_add_tail(&cp->cline->update, &cp->update); - cp->update_flags |= CON_UPDATE_LIST; - } -} - -static inline void -con3270_cline_end(struct con3270 *cp) -{ - struct string *s; - unsigned int size; - - /* Copy cline. */ - size = (cp->cline->len < cp->view.cols - 5) ? - cp->cline->len + 4 : cp->view.cols; - s = con3270_alloc_string(cp, size); - memcpy(s->string, cp->cline->string, cp->cline->len); - if (cp->cline->len < cp->view.cols - 5) { - s->string[s->len - 4] = TO_RA; - s->string[s->len - 1] = 0; - } else { - while (--size >= cp->cline->len) - s->string[size] = cp->view.ascebc[' ']; - } - /* Replace cline with allocated line s and reset cline. */ - list_add(&s->list, &cp->cline->list); - list_del_init(&cp->cline->list); - if (!list_empty(&cp->cline->update)) { - list_add(&s->update, &cp->cline->update); - list_del_init(&cp->cline->update); - } - cp->cline->len = 0; -} - -/* - * Write a string to the 3270 console - */ -static void -con3270_write(struct console *co, const char *str, unsigned int count) -{ - struct con3270 *cp; - unsigned long flags; - unsigned char c; - - cp = condev; - spin_lock_irqsave(&cp->view.lock, flags); - while (count-- > 0) { - c = *str++; - if (cp->cline->len == 0) - con3270_cline_add(cp); - if (c != '\n') - con3270_cline_insert(cp, c); - if (c == '\n' || cp->cline->len >= cp->view.cols) - con3270_cline_end(cp); - } - /* Setup timer to output current console buffer after 1/10 second */ - cp->nr_up = 0; - if (cp->view.dev && !timer_pending(&cp->timer)) - con3270_set_timer(cp, HZ/10); - spin_unlock_irqrestore(&cp->view.lock,flags); -} - -static struct tty_driver * -con3270_device(struct console *c, int *index) -{ - *index = c->index; - return tty3270_driver; -} - -/* - * Wait for end of write request. - */ -static void -con3270_wait_write(struct con3270 *cp) -{ - while (!cp->write) { - raw3270_wait_cons_dev(cp->view.dev); - barrier(); - } -} - -/* - * The below function is called as a panic/reboot notifier before the - * system enters a disabled, endless loop. - * - * Notice we must use the spin_trylock() alternative, to prevent lockups - * in atomic context (panic routine runs with secondary CPUs, local IRQs - * and preemption disabled). - */ -static int con3270_notify(struct notifier_block *self, - unsigned long event, void *data) -{ - struct con3270 *cp; - unsigned long flags; - - cp = condev; - if (!cp->view.dev) - return NOTIFY_DONE; - if (!raw3270_view_lock_unavailable(&cp->view)) - raw3270_activate_view(&cp->view); - if (!spin_trylock_irqsave(&cp->view.lock, flags)) - return NOTIFY_DONE; - con3270_wait_write(cp); - cp->nr_up = 0; - con3270_rebuild_update(cp); - con3270_update_status(cp); - while (cp->update_flags != 0) { - spin_unlock_irqrestore(&cp->view.lock, flags); - con3270_update(&cp->timer); - spin_lock_irqsave(&cp->view.lock, flags); - con3270_wait_write(cp); - } - spin_unlock_irqrestore(&cp->view.lock, flags); - - return NOTIFY_DONE; -} - -static struct notifier_block on_panic_nb = { - .notifier_call = con3270_notify, - .priority = INT_MIN + 1, /* run the callback late */ -}; - -static struct notifier_block on_reboot_nb = { - .notifier_call = con3270_notify, - .priority = INT_MIN + 1, /* run the callback late */ -}; - -/* - * The console structure for the 3270 console - */ -static struct console con3270 = { - .name = "tty3270", - .write = con3270_write, - .device = con3270_device, - .flags = CON_PRINTBUFFER, -}; - -/* - * 3270 console initialization code called from console_init(). - */ -static int __init -con3270_init(void) -{ - struct raw3270 *rp; - void *cbuf; - int i; - - /* Check if 3270 is to be the console */ - if (!CONSOLE_IS_3270) - return -ENODEV; - - /* Set the console mode for VM */ - if (MACHINE_IS_VM) { - cpcmd("TERM CONMODE 3270", NULL, 0, NULL); - cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); - } - - rp = raw3270_setup_console(); - if (IS_ERR(rp)) - return PTR_ERR(rp); - - condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA); - if (!condev) - return -ENOMEM; - condev->view.dev = rp; - - condev->read = raw3270_request_alloc(0); - condev->read->callback = con3270_read_callback; - condev->read->callback_data = condev; - condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE); - condev->kreset = raw3270_request_alloc(1); - - INIT_LIST_HEAD(&condev->lines); - INIT_LIST_HEAD(&condev->update); - timer_setup(&condev->timer, con3270_update, 0); - tasklet_init(&condev->readlet, con3270_read_tasklet, - (unsigned long) condev->read); - - raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ); - - INIT_LIST_HEAD(&condev->freemem); - for (i = 0; i < CON3270_STRING_PAGES; i++) { - cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); - } - condev->cline = alloc_string(&condev->freemem, condev->view.cols); - condev->cline->len = 0; - con3270_create_status(condev); - condev->input = alloc_string(&condev->freemem, 80); - atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); - register_reboot_notifier(&on_reboot_nb); - register_console(&con3270); - return 0; -} - -console_initcall(con3270_init); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 3ea5df58b64b..1932077e9650 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -17,7 +17,8 @@ #include #include #include - +#include +#include #include #include #include @@ -25,19 +26,19 @@ #include #include #include +#include #include #include "raw3270.h" -#include "tty3270.h" #include "keyboard.h" #define TTY3270_CHAR_BUF_SIZE 256 #define TTY3270_OUTPUT_BUFFER_SIZE 1024 #define TTY3270_STRING_PAGES 5 -struct tty_driver *tty3270_driver; +static struct tty_driver *tty3270_driver; static int tty3270_max_index; - +static struct tty3270 *condev; static struct raw3270_fn tty3270_fn; struct tty3270_cell { @@ -378,7 +379,7 @@ tty3270_update(struct timer_list *t) return; } - spin_lock(&tp->view.lock); + spin_lock_irq(&tp->view.lock); updated = 0; if (tp->update_flags & TTY_UPDATE_ALL) { tty3270_rebuild_update(tp); @@ -449,7 +450,7 @@ tty3270_update(struct timer_list *t) raw3270_request_reset(wrq); xchg(&tp->write, wrq); } - spin_unlock(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); } /* @@ -481,7 +482,7 @@ tty3270_rcl_backward(struct kbd_data *kbd) struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); struct string *s; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); if (tp->inattr == TF_INPUT) { if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) tp->rcl_walk = tp->rcl_walk->prev; @@ -496,7 +497,7 @@ tty3270_rcl_backward(struct kbd_data *kbd) tty3270_update_prompt(tp, NULL, 0); tty3270_set_timer(tp, 1); } - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); } /* @@ -519,7 +520,7 @@ tty3270_scroll_forward(struct kbd_data *kbd) struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); int nr_up; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); nr_up = tp->nr_up - tp->view.rows + 2; if (nr_up < 0) nr_up = 0; @@ -529,7 +530,7 @@ tty3270_scroll_forward(struct kbd_data *kbd) tty3270_update_status(tp); tty3270_set_timer(tp, 1); } - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); } /* @@ -541,7 +542,7 @@ tty3270_scroll_backward(struct kbd_data *kbd) struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); int nr_up; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); nr_up = tp->nr_up + tp->view.rows - 2; if (nr_up + tp->view.rows - 2 > tp->nr_lines) nr_up = tp->nr_lines - tp->view.rows + 2; @@ -551,7 +552,7 @@ tty3270_scroll_backward(struct kbd_data *kbd) tty3270_update_status(tp); tty3270_set_timer(tp, 1); } - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); } /* @@ -566,7 +567,7 @@ tty3270_read_tasklet(unsigned long data) char *input; int len; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); /* * Two AID keys are special: For 0x7d (enter) the input line * has to be emitted to the tty and for 0x6d the screen @@ -593,7 +594,7 @@ tty3270_read_tasklet(unsigned long data) tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); } - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); /* Start keyboard reset command. */ raw3270_request_reset(tp->kreset); @@ -857,7 +858,7 @@ static void tty3270_resize_work(struct work_struct *work) if (IS_ERR(screen)) return; /* Switch to new output size */ - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); tty3270_blank_screen(tp); oscreen = tp->screen; orows = tp->view.rows; @@ -872,7 +873,7 @@ static void tty3270_resize_work(struct work_struct *work) while (tp->nr_lines < tp->view.rows - 2) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); tty3270_free_screen(oscreen, orows); tty3270_set_timer(tp, 1); /* Informat tty layer about new size */ @@ -969,7 +970,7 @@ tty3270_create_view(int index, struct tty3270 **newtp) rc = raw3270_add_view(&tp->view, &tty3270_fn, index + RAW3270_FIRSTMINOR, - RAW3270_VIEW_LOCK_BH); + RAW3270_VIEW_LOCK_IRQ); if (rc) { tty3270_free_view(tp); return rc; @@ -1648,7 +1649,7 @@ tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, { int i_msg, i; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); for (i_msg = 0; !tty->flow.stopped && i_msg < count; i_msg++) { if (tp->esc_state != 0) { /* Continue escape sequence. */ @@ -1710,7 +1711,7 @@ tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, if (!timer_pending(&tp->timer)) tty3270_set_timer(tp, HZ/10); - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); } /* @@ -1777,7 +1778,7 @@ tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) tp = tty->driver_data; if (!tp) return; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); if (L_ICANON(tty)) { new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; if (new != tp->inattr) { @@ -1786,7 +1787,7 @@ tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) tty3270_set_timer(tp, 1); } } - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); } /* @@ -1830,7 +1831,7 @@ tty3270_hangup(struct tty_struct *tty) tp = tty->driver_data; if (!tp) return; - spin_lock_bh(&tp->view.lock); + spin_lock_irq(&tp->view.lock); tp->cx = tp->saved_cx = 0; tp->cy = tp->saved_cy = 0; tp->highlight = tp->saved_highlight = TAX_RESET; @@ -1839,7 +1840,7 @@ tty3270_hangup(struct tty_struct *tty) while (tp->nr_lines < tp->view.rows - 2) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; - spin_unlock_bh(&tp->view.lock); + spin_unlock_irq(&tp->view.lock); tty3270_set_timer(tp, 1); } @@ -1965,6 +1966,140 @@ tty3270_exit(void) tty3270_del_views(); } +#if IS_ENABLED(CONFIG_TN3270_CONSOLE) +static void +con3270_write(struct console *co, const char *str, unsigned int count) +{ + struct tty3270 *tp = co->data; + unsigned long flags; + char c; + + spin_lock_irqsave(&tp->view.lock, flags); + while (count--) { + c = *str++; + if (c == 0x0a) { + tty3270_cr(tp); + tty3270_lf(tp); + } else { + if (tp->cx >= tp->view.cols) { + tty3270_cr(tp); + tty3270_lf(tp); + } + tty3270_put_character(tp, c); + tp->cx++; + } + } + spin_unlock_irqrestore(&tp->view.lock, flags); +} + +static struct tty_driver * +con3270_device(struct console *c, int *index) +{ + *index = c->index; + return tty3270_driver; +} + +static void +con3270_wait_write(struct tty3270 *tp) +{ + while (!tp->write) { + raw3270_wait_cons_dev(tp->view.dev); + barrier(); + } +} + +/* + * The below function is called as a panic/reboot notifier before the + * system enters a disabled, endless loop. + * + * Notice we must use the spin_trylock() alternative, to prevent lockups + * in atomic context (panic routine runs with secondary CPUs, local IRQs + * and preemption disabled). + */ +static int con3270_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + struct tty3270 *tp; + unsigned long flags; + + tp = condev; + if (!tp->view.dev) + return NOTIFY_DONE; + if (!raw3270_view_lock_unavailable(&tp->view)) + raw3270_activate_view(&tp->view); + if (!spin_trylock_irqsave(&tp->view.lock, flags)) + return NOTIFY_DONE; + con3270_wait_write(tp); + tp->nr_up = 0; + while (tp->update_flags != 0) { + spin_unlock_irqrestore(&tp->view.lock, flags); + tty3270_update(&tp->timer); + spin_lock_irqsave(&tp->view.lock, flags); + con3270_wait_write(tp); + } + spin_unlock_irqrestore(&tp->view.lock, flags); + return NOTIFY_DONE; +} + +static struct notifier_block on_panic_nb = { + .notifier_call = con3270_notify, + .priority = INT_MIN + 1, /* run the callback late */ +}; + +static struct notifier_block on_reboot_nb = { + .notifier_call = con3270_notify, + .priority = INT_MIN + 1, /* run the callback late */ +}; + +static struct console con3270 = { + .name = "tty3270", + .write = con3270_write, + .device = con3270_device, + .flags = CON_PRINTBUFFER, +}; + +static int __init +con3270_init(void) +{ + struct raw3270_view *view; + struct raw3270 *rp; + struct tty3270 *tp; + int rc; + + /* Check if 3270 is to be the console */ + if (!CONSOLE_IS_3270) + return -ENODEV; + + /* Set the console mode for VM */ + if (MACHINE_IS_VM) { + cpcmd("TERM CONMODE 3270", NULL, 0, NULL); + cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); + } + + rp = raw3270_setup_console(); + if (IS_ERR(rp)) + return PTR_ERR(rp); + + /* Check if the tty3270 is already there. */ + view = raw3270_find_view(&tty3270_fn, RAW3270_FIRSTMINOR); + if (IS_ERR(view)) { + rc = tty3270_create_view(0, &tp); + if (rc) + return rc; + } else { + tp = container_of(view, struct tty3270, view); + tp->inattr = TF_INPUT; + } + con3270.data = tp; + condev = tp; + atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); + register_reboot_notifier(&on_reboot_nb); + register_console(&con3270); + return 0; +} +console_initcall(con3270_init); +#endif + MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); diff --git a/drivers/s390/char/tty3270.h b/drivers/s390/char/tty3270.h deleted file mode 100644 index 52ceed6f8408..000000000000 --- a/drivers/s390/char/tty3270.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright IBM Corp. 2007 - * - */ - -#ifndef __DRIVERS_S390_CHAR_TTY3270_H -#define __DRIVERS_S390_CHAR_TTY3270_H - -#include -#include - -extern struct tty_driver *tty3270_driver; - -#endif /* __DRIVERS_S390_CHAR_TTY3270_H */ From 9603cb334a7dc30fb544a3579bcbf23ae5b04f8f Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:35:40 +0100 Subject: [PATCH 013/182] s390/tty3270: rename to con3270 Rename the driver file to con3270 to be in sync with con3215.c. This removes CONFIG_TN3270_TTY, it is now always build if CONFIG_TN3270 is enabled. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/Kconfig | 11 ++--------- drivers/s390/char/Makefile | 3 +-- drivers/s390/char/{tty3270.c => con3270.c} | 0 3 files changed, 3 insertions(+), 11 deletions(-) rename drivers/s390/char/{tty3270.c => con3270.c} (100%) diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 7d1749b0d378..80c4e5101c97 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -5,17 +5,10 @@ comment "S/390 character device drivers" config TN3270 def_tristate y prompt "Support for locally attached 3270 terminals" - depends on CCW + depends on CCW && TTY help Include support for IBM 3270 terminals. -config TN3270_TTY - def_tristate y - prompt "Support for tty input/output on 3270 terminals" - depends on TN3270 && TTY - help - Include support for using an IBM 3270 terminal as a Linux tty. - config TN3270_FS def_tristate m prompt "Support for fullscreen applications on 3270 terminals" @@ -26,7 +19,7 @@ config TN3270_FS config TN3270_CONSOLE def_bool y prompt "Support for console on 3270 terminal" - depends on TN3270=y && TN3270_TTY=y + depends on TN3270=y help Include support for using an IBM 3270 terminal as a Linux system console. Available only if 3270 support is compiled in statically. diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index e3bcbbb98b9f..b0f6b3201636 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -21,8 +21,7 @@ obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \ sclp_early.o sclp_early_core.o sclp_sd.o -obj-$(CONFIG_TN3270) += raw3270.o -obj-$(CONFIG_TN3270_TTY) += tty3270.o +obj-$(CONFIG_TN3270) += raw3270.o con3270.o obj-$(CONFIG_TN3270_FS) += fs3270.o obj-$(CONFIG_TN3215) += con3215.o diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/con3270.c similarity index 100% rename from drivers/s390/char/tty3270.c rename to drivers/s390/char/con3270.c From fbaee7464fbb61a4cb484d9c41f14517738c80c7 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 17 Nov 2022 22:02:01 +0100 Subject: [PATCH 014/182] s390/tty3270: add support for diag 8c The current code uses diag210 to infer the 3270 geometry from the model number when running on z/VM. This doesn't work well as almost all 3270 software clients report as 3279-2 with a custom resolution. tty3270 assumes it has a 80x24 terminal connected because of the -2 suffix. Use diag 8c to fetch the realy geometry from z/VM. Note that this doesn't allow dynamic resizing, i.e. reconnecting to a z/VM session with a different geometry. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/diag.h | 14 ++++++++++++++ arch/s390/kernel/diag.c | 26 ++++++++++++++++++++++++++ arch/s390/kernel/text_amode31.S | 13 +++++++++++++ drivers/s390/char/raw3270.c | 11 +++++++++++ 4 files changed, 64 insertions(+) diff --git a/arch/s390/include/asm/diag.h b/arch/s390/include/asm/diag.h index 56e99c286d12..9488b7ffb21c 100644 --- a/arch/s390/include/asm/diag.h +++ b/arch/s390/include/asm/diag.h @@ -12,6 +12,7 @@ #include #include #include +#include enum diag_stat_enum { DIAG_STAT_X008, @@ -20,6 +21,7 @@ enum diag_stat_enum { DIAG_STAT_X014, DIAG_STAT_X044, DIAG_STAT_X064, + DIAG_STAT_X08C, DIAG_STAT_X09C, DIAG_STAT_X0DC, DIAG_STAT_X204, @@ -83,6 +85,16 @@ struct diag210 { extern int diag210(struct diag210 *addr); +struct diag8c { + u8 flags; + u8 num_partitions; + u16 width; + u16 height; + u8 data[0]; +} __packed __aligned(4); + +extern int diag8c(struct diag8c *out, struct ccw_dev_id *devno); + /* bit is set in flags, when physical cpu info is included in diag 204 data */ #define DIAG204_LPAR_PHYS_FLG 0x80 #define DIAG204_LPAR_NAME_LEN 8 /* lpar name len in diag 204 data */ @@ -318,6 +330,7 @@ struct diag_ops { int (*diag210)(struct diag210 *addr); int (*diag26c)(void *req, void *resp, enum diag26c_sc subcode); int (*diag14)(unsigned long rx, unsigned long ry1, unsigned long subcode); + int (*diag8c)(struct diag8c *addr, struct ccw_dev_id *devno, size_t len); void (*diag0c)(struct hypfs_diag0c_entry *entry); void (*diag308_reset)(void); }; @@ -330,5 +343,6 @@ int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode); int _diag14_amode31(unsigned long rx, unsigned long ry1, unsigned long subcode); void _diag0c_amode31(struct hypfs_diag0c_entry *entry); void _diag308_reset_amode31(void); +int _diag8c_amode31(struct diag8c *addr, struct ccw_dev_id *devno, size_t len); #endif /* _ASM_S390_DIAG_H */ diff --git a/arch/s390/kernel/diag.c b/arch/s390/kernel/diag.c index a778714e4d8b..a6a0222a35cf 100644 --- a/arch/s390/kernel/diag.c +++ b/arch/s390/kernel/diag.c @@ -35,6 +35,7 @@ static const struct diag_desc diag_map[NR_DIAG_STAT] = { [DIAG_STAT_X014] = { .code = 0x014, .name = "Spool File Services" }, [DIAG_STAT_X044] = { .code = 0x044, .name = "Voluntary Timeslice End" }, [DIAG_STAT_X064] = { .code = 0x064, .name = "NSS Manipulation" }, + [DIAG_STAT_X08C] = { .code = 0x08c, .name = "Access 3270 Display Device Information" }, [DIAG_STAT_X09C] = { .code = 0x09c, .name = "Relinquish Timeslice" }, [DIAG_STAT_X0DC] = { .code = 0x0dc, .name = "Appldata Control" }, [DIAG_STAT_X204] = { .code = 0x204, .name = "Logical-CPU Utilization" }, @@ -57,12 +58,16 @@ struct diag_ops __amode31_ref diag_amode31_ops = { .diag26c = _diag26c_amode31, .diag14 = _diag14_amode31, .diag0c = _diag0c_amode31, + .diag8c = _diag8c_amode31, .diag308_reset = _diag308_reset_amode31 }; static struct diag210 _diag210_tmp_amode31 __section(".amode31.data"); struct diag210 __amode31_ref *__diag210_tmp_amode31 = &_diag210_tmp_amode31; +static struct diag8c _diag8c_tmp_amode31 __section(".amode31.data"); +struct diag8c __amode31_ref *__diag8c_tmp_amode31 = &_diag8c_tmp_amode31; + static int show_diag_stat(struct seq_file *m, void *v) { struct diag_stat *stat; @@ -194,6 +199,27 @@ int diag210(struct diag210 *addr) } EXPORT_SYMBOL(diag210); +/* + * Diagnose 210: Get information about a virtual device + */ +int diag8c(struct diag8c *addr, struct ccw_dev_id *devno) +{ + static DEFINE_SPINLOCK(diag8c_lock); + unsigned long flags; + int ccode; + + spin_lock_irqsave(&diag8c_lock, flags); + + diag_stat_inc(DIAG_STAT_X08C); + ccode = diag_amode31_ops.diag8c(__diag8c_tmp_amode31, devno, sizeof(*addr)); + + *addr = *__diag8c_tmp_amode31; + spin_unlock_irqrestore(&diag8c_lock, flags); + + return ccode; +} +EXPORT_SYMBOL(diag8c); + int diag224(void *ptr) { int rc = -EOPNOTSUPP; diff --git a/arch/s390/kernel/text_amode31.S b/arch/s390/kernel/text_amode31.S index 2c8b14cc5556..e0f01ce251f5 100644 --- a/arch/s390/kernel/text_amode31.S +++ b/arch/s390/kernel/text_amode31.S @@ -62,6 +62,19 @@ ENTRY(_diag210_amode31) EX_TABLE_AMODE31(.Ldiag210_ex, .Ldiag210_fault) ENDPROC(_diag210_amode31) +/* + * int diag8c(struct diag8c *addr, struct ccw_dev_id *devno, size_t len) +*/ +ENTRY(_diag8c_amode31) + llgf %r3,0(%r3) + sam31 + diag %r2,%r4,0x8c +.Ldiag8c_ex: + sam64 + lgfr %r2,%r3 + BR_EX_AMODE31_r14 + EX_TABLE_AMODE31(.Ldiag8c_ex, .Ldiag8c_ex) +ENDPROC(_diag8c_amode31) /* * int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode) */ diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index fb3f62ac8be4..8e6742184cc8 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -421,8 +421,19 @@ raw3270_size_device_vm(struct raw3270 *rp) int rc, model; struct ccw_dev_id dev_id; struct diag210 diag_data; + struct diag8c diag8c_data; ccw_device_get_id(rp->cdev, &dev_id); + rc = diag8c(&diag8c_data, &dev_id); + if (!rc) { + rp->model = 2; + rp->rows = diag8c_data.height; + rp->cols = diag8c_data.width; + if (diag8c_data.flags & 1) + set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags); + return; + } + diag_data.vrdcdvno = dev_id.devno; diag_data.vrdclen = sizeof(struct diag210); rc = diag210(&diag_data); From e6d98bb823af51ad8b89ee705111dbb1ed8a4aff Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 22:32:35 +0100 Subject: [PATCH 015/182] s390/con3270: fix formatting issues fix function prototypes split over two lines like: static void foobar(void) and fix superfluous spaces in declarations like foo * bar Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 172 ++++++++++++------------------------ 1 file changed, 58 insertions(+), 114 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 1932077e9650..2ccfe14765e6 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -134,8 +134,7 @@ static void tty3270_set_timer(struct tty3270 *tp, int expires) /* * The input line are the two last lines of the screen. */ -static void -tty3270_update_prompt(struct tty3270 *tp, char *input, int count) +static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) { struct string *line; unsigned int off; @@ -161,8 +160,7 @@ tty3270_update_prompt(struct tty3270 *tp, char *input, int count) tp->update_flags |= TTY_UPDATE_INPUT; } -static void -tty3270_create_prompt(struct tty3270 *tp) +static void tty3270_create_prompt(struct tty3270 *tp) { static const unsigned char blueprint[] = { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT, @@ -192,8 +190,7 @@ tty3270_create_prompt(struct tty3270 *tp) * The status line is the last line of the screen. It shows the string * "Running"/"Holding" in the lower right corner of the screen. */ -static void -tty3270_update_status(struct tty3270 * tp) +static void tty3270_update_status(struct tty3270 *tp) { char *str; @@ -203,8 +200,7 @@ tty3270_update_status(struct tty3270 * tp) tp->update_flags |= TTY_UPDATE_STATUS; } -static void -tty3270_create_status(struct tty3270 * tp) +static void tty3270_create_status(struct tty3270 *tp) { static const unsigned char blueprint[] = { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN, @@ -226,8 +222,7 @@ tty3270_create_status(struct tty3270 * tp) * Set output offsets to 3270 datastream fragment of a tty string. * (TO_SBA offset at the start and TO_RA offset at the end of the string) */ -static void -tty3270_update_string(struct tty3270 *tp, struct string *line, int nr) +static void tty3270_update_string(struct tty3270 *tp, struct string *line, int nr) { unsigned char *cp; @@ -242,8 +237,7 @@ tty3270_update_string(struct tty3270 *tp, struct string *line, int nr) /* * Rebuild update list to print all lines. */ -static void -tty3270_rebuild_update(struct tty3270 *tp) +static void tty3270_rebuild_update(struct tty3270 *tp) { struct string *s, *n; int line, nr_up; @@ -273,8 +267,7 @@ tty3270_rebuild_update(struct tty3270 *tp) * Alloc string for size bytes. If there is not enough room in * freemem, free strings until there is room. */ -static struct string * -tty3270_alloc_string(struct tty3270 *tp, size_t size) +static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size) { struct string *s, *n; @@ -304,8 +297,7 @@ tty3270_alloc_string(struct tty3270 *tp, size_t size) /* * Add an empty line to the list. */ -static void -tty3270_blank_line(struct tty3270 *tp) +static void tty3270_blank_line(struct tty3270 *tp) { static const unsigned char blueprint[] = { TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET, @@ -324,8 +316,7 @@ tty3270_blank_line(struct tty3270 *tp) /* * Create a blank screen and remove all lines from the history. */ -static void -tty3270_blank_screen(struct tty3270 *tp) +static void tty3270_blank_screen(struct tty3270 *tp) { struct string *s, *n; int i; @@ -345,8 +336,7 @@ tty3270_blank_screen(struct tty3270 *tp) /* * Write request completion callback. */ -static void -tty3270_write_callback(struct raw3270_request *rq, void *data) +static void tty3270_write_callback(struct raw3270_request *rq, void *data) { struct tty3270 *tp = container_of(rq->view, struct tty3270, view); @@ -362,8 +352,7 @@ tty3270_write_callback(struct raw3270_request *rq, void *data) /* * Update 3270 display. */ -static void -tty3270_update(struct timer_list *t) +static void tty3270_update(struct timer_list *t) { struct tty3270 *tp = from_timer(tp, t, timer); static char invalid_sba[2] = { 0xff, 0xff }; @@ -456,8 +445,7 @@ tty3270_update(struct timer_list *t) /* * Command recalling. */ -static void -tty3270_rcl_add(struct tty3270 *tp, char *input, int len) +static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len) { struct string *s; @@ -476,8 +464,7 @@ tty3270_rcl_add(struct tty3270 *tp, char *input, int len) tp->rcl_nr++; } -static void -tty3270_rcl_backward(struct kbd_data *kbd) +static void tty3270_rcl_backward(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); struct string *s; @@ -503,8 +490,7 @@ tty3270_rcl_backward(struct kbd_data *kbd) /* * Deactivate tty view. */ -static void -tty3270_exit_tty(struct kbd_data *kbd) +static void tty3270_exit_tty(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); @@ -514,8 +500,7 @@ tty3270_exit_tty(struct kbd_data *kbd) /* * Scroll forward in history. */ -static void -tty3270_scroll_forward(struct kbd_data *kbd) +static void tty3270_scroll_forward(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); int nr_up; @@ -536,8 +521,7 @@ tty3270_scroll_forward(struct kbd_data *kbd) /* * Scroll backward in history. */ -static void -tty3270_scroll_backward(struct kbd_data *kbd) +static void tty3270_scroll_backward(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); int nr_up; @@ -558,8 +542,7 @@ tty3270_scroll_backward(struct kbd_data *kbd) /* * Pass input line to tty. */ -static void -tty3270_read_tasklet(unsigned long data) +static void tty3270_read_tasklet(unsigned long data) { struct raw3270_request *rrq = (struct raw3270_request *)data; static char kreset_data = TW_KR; @@ -615,8 +598,7 @@ tty3270_read_tasklet(unsigned long data) /* * Read request completion callback. */ -static void -tty3270_read_callback(struct raw3270_request *rq, void *data) +static void tty3270_read_callback(struct raw3270_request *rq, void *data) { struct tty3270 *tp = container_of(rq->view, struct tty3270, view); raw3270_get_view(rq->view); @@ -627,8 +609,7 @@ tty3270_read_callback(struct raw3270_request *rq, void *data) /* * Issue a read request. Call with device lock. */ -static void -tty3270_issue_read(struct tty3270 *tp, int lock) +static void tty3270_issue_read(struct tty3270 *tp, int lock) { struct raw3270_request *rrq; int rc; @@ -655,8 +636,7 @@ tty3270_issue_read(struct tty3270 *tp, int lock) /* * Hang up the tty */ -static void -tty3270_hangup_tasklet(unsigned long data) +static void tty3270_hangup_tasklet(unsigned long data) { struct tty3270 *tp = (struct tty3270 *)data; tty_port_tty_hangup(&tp->port, true); @@ -666,8 +646,7 @@ tty3270_hangup_tasklet(unsigned long data) /* * Switch to the tty view. */ -static int -tty3270_activate(struct raw3270_view *view) +static int tty3270_activate(struct raw3270_view *view) { struct tty3270 *tp = container_of(view, struct tty3270, view); @@ -676,16 +655,14 @@ tty3270_activate(struct raw3270_view *view) return 0; } -static void -tty3270_deactivate(struct raw3270_view *view) +static void tty3270_deactivate(struct raw3270_view *view) { struct tty3270 *tp = container_of(view, struct tty3270, view); del_timer(&tp->timer); } -static void -tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) +static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { @@ -714,8 +691,7 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) /* * Allocate tty3270 structure. */ -static struct tty3270 * -tty3270_alloc_view(void) +static struct tty3270 *tty3270_alloc_view(void) { struct tty3270 *tp; int pages; @@ -785,8 +761,7 @@ tty3270_alloc_view(void) /* * Free tty3270 structure. */ -static void -tty3270_free_view(struct tty3270 *tp) +static void tty3270_free_view(struct tty3270 *tp) { int pages; @@ -804,8 +779,7 @@ tty3270_free_view(struct tty3270 *tp) /* * Allocate tty3270 screen. */ -static struct tty3270_line * -tty3270_alloc_screen(unsigned int rows, unsigned int cols) +static struct tty3270_line *tty3270_alloc_screen(unsigned int rows, unsigned int cols) { struct tty3270_line *screen; unsigned long size; @@ -833,8 +807,7 @@ tty3270_alloc_screen(unsigned int rows, unsigned int cols) /* * Free tty3270 screen. */ -static void -tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) +static void tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) { int lines; @@ -886,8 +859,7 @@ static void tty3270_resize_work(struct work_struct *work) tty_kref_put(tty); } -static void -tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) +static void tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) { struct tty3270 *tp = container_of(view, struct tty3270, view); @@ -902,8 +874,7 @@ tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) /* * Unlink tty3270 data structure from tty. */ -static void -tty3270_release(struct raw3270_view *view) +static void tty3270_release(struct raw3270_view *view) { struct tty3270 *tp = container_of(view, struct tty3270, view); struct tty_struct *tty = tty_port_tty_get(&tp->port); @@ -920,8 +891,7 @@ tty3270_release(struct raw3270_view *view) /* * Free tty3270 data structure */ -static void -tty3270_free(struct raw3270_view *view) +static void tty3270_free(struct raw3270_view *view) { struct tty3270 *tp = container_of(view, struct tty3270, view); @@ -933,8 +903,7 @@ tty3270_free(struct raw3270_view *view) /* * Delayed freeing of tty3270 views. */ -static void -tty3270_del_views(void) +static void tty3270_del_views(void) { int i; @@ -1042,8 +1011,7 @@ tty3270_install(struct tty_driver *driver, struct tty_struct *tty) /* * This routine is called whenever a 3270 tty is opened. */ -static int -tty3270_open(struct tty_struct *tty, struct file *filp) +static int tty3270_open(struct tty_struct *tty, struct file *filp) { struct tty3270 *tp = tty->driver_data; struct tty_port *port = &tp->port; @@ -1057,8 +1025,7 @@ tty3270_open(struct tty_struct *tty, struct file *filp) * This routine is called when the 3270 tty is closed. We wait * for the remaining request to be completed. Then we clean up. */ -static void -tty3270_close(struct tty_struct *tty, struct file * filp) +static void tty3270_close(struct tty_struct *tty, struct file *filp) { struct tty3270 *tp = tty->driver_data; @@ -1081,8 +1048,7 @@ static void tty3270_cleanup(struct tty_struct *tty) /* * We always have room. */ -static unsigned int -tty3270_write_room(struct tty_struct *tty) +static unsigned int tty3270_write_room(struct tty_struct *tty) { return INT_MAX; } @@ -1116,8 +1082,7 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) /* * Convert a tty3270_line to a 3270 data fragment usable for output. */ -static void -tty3270_convert_line(struct tty3270 *tp, int line_nr) +static void tty3270_convert_line(struct tty3270 *tp, int line_nr) { struct tty3270_line *line; struct tty3270_cell *cell; @@ -1223,8 +1188,7 @@ tty3270_convert_line(struct tty3270 *tp, int line_nr) /* * Do carriage return. */ -static void -tty3270_cr(struct tty3270 *tp) +static void tty3270_cr(struct tty3270 *tp) { tp->cx = 0; } @@ -1232,8 +1196,7 @@ tty3270_cr(struct tty3270 *tp) /* * Do line feed. */ -static void -tty3270_lf(struct tty3270 *tp) +static void tty3270_lf(struct tty3270 *tp) { struct tty3270_line temp; int i; @@ -1253,8 +1216,7 @@ tty3270_lf(struct tty3270 *tp) tty3270_rebuild_update(tp); } -static void -tty3270_ri(struct tty3270 *tp) +static void tty3270_ri(struct tty3270 *tp) { if (tp->cy > 0) { tty3270_convert_line(tp, tp->cy); @@ -1265,8 +1227,7 @@ tty3270_ri(struct tty3270 *tp) /* * Insert characters at current position. */ -static void -tty3270_insert_characters(struct tty3270 *tp, int n) +static void tty3270_insert_characters(struct tty3270 *tp, int n) { struct tty3270_line *line; int k; @@ -1296,8 +1257,7 @@ tty3270_insert_characters(struct tty3270 *tp, int n) /* * Delete characters at current position. */ -static void -tty3270_delete_characters(struct tty3270 *tp, int n) +static void tty3270_delete_characters(struct tty3270 *tp, int n) { struct tty3270_line *line; int i; @@ -1317,8 +1277,7 @@ tty3270_delete_characters(struct tty3270 *tp, int n) /* * Erase characters at current position. */ -static void -tty3270_erase_characters(struct tty3270 *tp, int n) +static void tty3270_erase_characters(struct tty3270 *tp, int n) { struct tty3270_line *line; struct tty3270_cell *cell; @@ -1340,8 +1299,7 @@ tty3270_erase_characters(struct tty3270 *tp, int n) * Esc [ 1 K Erase from beginning of line to current position inclusive * Esc [ 2 K Erase entire line (without moving cursor) */ -static void -tty3270_erase_line(struct tty3270 *tp, int mode) +static void tty3270_erase_line(struct tty3270 *tp, int mode) { struct tty3270_line *line; struct tty3270_cell *cell; @@ -1370,8 +1328,7 @@ tty3270_erase_line(struct tty3270 *tp, int mode) * Esc [ 1 J Erase from top of screen to current position inclusive * Esc [ 2 J Erase entire screen (without moving the cursor) */ -static void -tty3270_erase_display(struct tty3270 *tp, int mode) +static void tty3270_erase_display(struct tty3270 *tp, int mode) { int i; @@ -1400,8 +1357,7 @@ tty3270_erase_display(struct tty3270 *tp, int mode) * Set attributes found in an escape sequence. * Esc [ ; ; ... m */ -static void -tty3270_set_attributes(struct tty3270 *tp) +static void tty3270_set_attributes(struct tty3270 *tp) { static unsigned char f_colors[] = { TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE, @@ -1454,14 +1410,12 @@ tty3270_set_attributes(struct tty3270 *tp) } } -static inline int -tty3270_getpar(struct tty3270 *tp, int ix) +static inline int tty3270_getpar(struct tty3270 *tp, int ix) { return (tp->esc_par[ix] > 0) ? tp->esc_par[ix] : 1; } -static void -tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) +static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) { int max_cx = max(0, cx); int max_cy = max(0, cy); @@ -1494,8 +1448,7 @@ tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) * Pn is a numeric parameter, a string of zero or more decimal digits. * Ps is a selective parameter. */ -static void -tty3270_escape_sequence(struct tty3270 *tp, char ch) +static void tty3270_escape_sequence(struct tty3270 *tp, char ch) { enum { ESnormal, ESesc, ESsquare, ESgetpars }; @@ -1643,9 +1596,8 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch) /* * String write routine for 3270 ttys */ -static void -tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, - const unsigned char *buf, int count) +static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, + const unsigned char *buf, int count) { int i_msg, i; @@ -1717,9 +1669,8 @@ tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, /* * String write routine for 3270 ttys */ -static int -tty3270_write(struct tty_struct * tty, - const unsigned char *buf, int count) +static int tty3270_write(struct tty_struct *tty, + const unsigned char *buf, int count) { struct tty3270 *tp; @@ -1752,8 +1703,7 @@ static int tty3270_put_char(struct tty_struct *tty, unsigned char ch) * Flush all characters from the ttys characeter buffer put there * by tty3270_put_char. */ -static void -tty3270_flush_chars(struct tty_struct *tty) +static void tty3270_flush_chars(struct tty_struct *tty) { struct tty3270 *tp; @@ -1769,8 +1719,7 @@ tty3270_flush_chars(struct tty_struct *tty) /* * Check for visible/invisible input switches */ -static void -tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) +static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) { struct tty3270 *tp; int new; @@ -1793,8 +1742,7 @@ tty3270_set_termios(struct tty_struct *tty, const struct ktermios *old) /* * Disable reading from a 3270 tty */ -static void -tty3270_throttle(struct tty_struct * tty) +static void tty3270_throttle(struct tty_struct *tty) { struct tty3270 *tp; @@ -1807,8 +1755,7 @@ tty3270_throttle(struct tty_struct * tty) /* * Enable reading from a 3270 tty */ -static void -tty3270_unthrottle(struct tty_struct * tty) +static void tty3270_unthrottle(struct tty_struct *tty) { struct tty3270 *tp; @@ -1823,8 +1770,7 @@ tty3270_unthrottle(struct tty_struct * tty) /* * Hang up the tty device. */ -static void -tty3270_hangup(struct tty_struct *tty) +static void tty3270_hangup(struct tty_struct *tty) { struct tty3270 *tp; @@ -1844,8 +1790,7 @@ tty3270_hangup(struct tty_struct *tty) tty3270_set_timer(tp, 1); } -static void -tty3270_wait_until_sent(struct tty_struct *tty, int timeout) +static void tty3270_wait_until_sent(struct tty_struct *tty, int timeout) { } @@ -1953,8 +1898,7 @@ static int __init tty3270_init(void) return 0; } -static void __exit -tty3270_exit(void) +static void __exit tty3270_exit(void) { struct tty_driver *driver; From 13d4999ab2fbb97f31ef66501cea82d16c9f3a94 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 22:38:49 +0100 Subject: [PATCH 016/182] s390/raw3270: fix formatting issues fix function prototypes split over two lines like: static void foobar(void) and fix superfluous spaces in declarations like foo * bar Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 140 +++++++++++++----------------------- 1 file changed, 51 insertions(+), 89 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 8e6742184cc8..dfb4ec150797 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -111,8 +111,7 @@ static inline int raw3270_state_ready(struct raw3270 *rp) return rp->state == RAW3270_STATE_READY; } -void -raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) +void raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) { if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) { cp[0] = (addr >> 8) & 0x3f; @@ -126,8 +125,7 @@ raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) /* * Allocate a new 3270 ccw request */ -struct raw3270_request * -raw3270_request_alloc(size_t size) +struct raw3270_request *raw3270_request_alloc(size_t size) { struct raw3270_request *rq; @@ -159,8 +157,7 @@ raw3270_request_alloc(size_t size) /* * Free 3270 ccw request */ -void -raw3270_request_free (struct raw3270_request *rq) +void raw3270_request_free(struct raw3270_request *rq) { kfree(rq->buffer); kfree(rq); @@ -169,8 +166,7 @@ raw3270_request_free (struct raw3270_request *rq) /* * Reset request to initial state. */ -void -raw3270_request_reset(struct raw3270_request *rq) +void raw3270_request_reset(struct raw3270_request *rq) { BUG_ON(!list_empty(&rq->list)); rq->ccw.cmd_code = 0; @@ -184,8 +180,7 @@ raw3270_request_reset(struct raw3270_request *rq) /* * Set command code to ccw of a request. */ -void -raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) +void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) { rq->ccw.cmd_code = cmd; } @@ -193,8 +188,7 @@ raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) /* * Add data fragment to output buffer. */ -int -raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) +int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) { if (size + rq->ccw.count > rq->size) return -E2BIG; @@ -206,8 +200,7 @@ raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size) /* * Set address/length pair to ccw of a request. */ -void -raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) +void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) { rq->ccw.cda = __pa(data); rq->ccw.count = size; @@ -216,8 +209,7 @@ raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size) /* * Set idal buffer to ccw of a request. */ -void -raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) +void raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) { rq->ccw.cda = __pa(ib->data); rq->ccw.count = ib->size; @@ -228,9 +220,8 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) * Add the request to the request queue, try to start it if the * 3270 device is idle. Return without waiting for end of i/o. */ -static int -__raw3270_start(struct raw3270 *rp, struct raw3270_view *view, - struct raw3270_request *rq) +static int __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, + struct raw3270_request *rq) { rq->view = view; raw3270_get_view(view); @@ -248,16 +239,14 @@ __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, return 0; } -int -raw3270_view_active(struct raw3270_view *view) +int raw3270_view_active(struct raw3270_view *view) { struct raw3270 *rp = view->dev; return rp && rp->view == view; } -int -raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) +int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) { unsigned long flags; struct raw3270 *rp; @@ -275,8 +264,7 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) return rc; } -int -raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) +int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) { struct raw3270 *rp; int rc; @@ -291,8 +279,7 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) return rc; } -int -raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) +int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) { struct raw3270 *rp; @@ -306,8 +293,7 @@ raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) /* * 3270 interrupt routine, called from the ccw_device layer */ -static void -raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) +static void raw3270_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) { struct raw3270 *rp; struct raw3270_view *view; @@ -415,8 +401,7 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ } __attribute__ ((packed)) aua; } __attribute__ ((packed)); -static void -raw3270_size_device_vm(struct raw3270 *rp) +static void raw3270_size_device_vm(struct raw3270 *rp) { int rc, model; struct ccw_dev_id dev_id; @@ -465,8 +450,7 @@ raw3270_size_device_vm(struct raw3270 *rp) } } -static void -raw3270_size_device(struct raw3270 *rp) +static void raw3270_size_device(struct raw3270 *rp) { struct raw3270_ua *uap; @@ -505,8 +489,7 @@ raw3270_size_device(struct raw3270 *rp) rp->model = 5; } -static void -raw3270_size_device_done(struct raw3270 *rp) +static void raw3270_size_device_done(struct raw3270 *rp) { struct raw3270_view *view; @@ -525,8 +508,7 @@ raw3270_size_device_done(struct raw3270 *rp) } } -static void -raw3270_read_modified_cb(struct raw3270_request *rq, void *data) +static void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; @@ -534,8 +516,7 @@ raw3270_read_modified_cb(struct raw3270_request *rq, void *data) raw3270_size_device_done(rp); } -static void -raw3270_read_modified(struct raw3270 *rp) +static void raw3270_read_modified(struct raw3270 *rp) { if (rp->state != RAW3270_STATE_W4ATTN) return; @@ -551,8 +532,7 @@ raw3270_read_modified(struct raw3270 *rp) raw3270_start_irq(&rp->init_view, &rp->init_readmod); } -static void -raw3270_writesf_readpart(struct raw3270 *rp) +static void raw3270_writesf_readpart(struct raw3270 *rp) { static const unsigned char wbuf[] = { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; @@ -572,8 +552,7 @@ raw3270_writesf_readpart(struct raw3270 *rp) /* * Device reset */ -static void -raw3270_reset_device_cb(struct raw3270_request *rq, void *data) +static void raw3270_reset_device_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; @@ -590,8 +569,7 @@ raw3270_reset_device_cb(struct raw3270_request *rq, void *data) memset(&rp->init_reset, 0, sizeof(rp->init_reset)); } -static int -__raw3270_reset_device(struct raw3270 *rp) +static int __raw3270_reset_device(struct raw3270 *rp) { int rc; @@ -611,8 +589,7 @@ __raw3270_reset_device(struct raw3270 *rp) return rc; } -static int -raw3270_reset_device(struct raw3270 *rp) +static int raw3270_reset_device(struct raw3270 *rp) { unsigned long flags; int rc; @@ -623,8 +600,7 @@ raw3270_reset_device(struct raw3270 *rp) return rc; } -int -raw3270_reset(struct raw3270_view *view) +int raw3270_reset(struct raw3270_view *view) { struct raw3270 *rp; int rc; @@ -639,8 +615,7 @@ raw3270_reset(struct raw3270_view *view) return rc; } -static void -__raw3270_disconnect(struct raw3270 *rp) +static void __raw3270_disconnect(struct raw3270 *rp) { struct raw3270_request *rq; struct raw3270_view *view; @@ -661,9 +636,8 @@ __raw3270_disconnect(struct raw3270 *rp) __raw3270_reset_device(rp); } -static void -raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, - struct irb *irb) +static void raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, + struct irb *irb) { struct raw3270 *rp; @@ -689,8 +663,8 @@ static struct raw3270_fn raw3270_init_fn = { /* * Setup new 3270 device. */ -static int -raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) +static int raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, + char *ascebc) { struct list_head *l; struct raw3270 *tmp; @@ -800,8 +774,7 @@ struct raw3270 __init *raw3270_setup_console(void) return rp; } -void -raw3270_wait_cons_dev(struct raw3270 *rp) +void raw3270_wait_cons_dev(struct raw3270 *rp) { unsigned long flags; @@ -815,8 +788,7 @@ raw3270_wait_cons_dev(struct raw3270 *rp) /* * Create a 3270 device structure. */ -static struct raw3270 * -raw3270_create_device(struct ccw_device *cdev) +static struct raw3270 *raw3270_create_device(struct ccw_device *cdev) { struct raw3270 *rp; char *ascebc; @@ -859,8 +831,7 @@ int raw3270_view_lock_unavailable(struct raw3270_view *view) /* * Activate a view. */ -int -raw3270_activate_view(struct raw3270_view *view) +int raw3270_activate_view(struct raw3270_view *view) { struct raw3270 *rp; struct raw3270_view *oldview, *nv; @@ -905,8 +876,7 @@ raw3270_activate_view(struct raw3270_view *view) /* * Deactivate current view. */ -void -raw3270_deactivate_view(struct raw3270_view *view) +void raw3270_deactivate_view(struct raw3270_view *view) { unsigned long flags; struct raw3270 *rp; @@ -937,8 +907,8 @@ raw3270_deactivate_view(struct raw3270_view *view) /* * Add view to device with minor "minor". */ -int -raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, int subclass) +int raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, + int minor, int subclass) { unsigned long flags; struct raw3270 *rp; @@ -973,8 +943,7 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, in /* * Find specific view of device with minor "minor". */ -struct raw3270_view * -raw3270_find_view(struct raw3270_fn *fn, int minor) +struct raw3270_view *raw3270_find_view(struct raw3270_fn *fn, int minor) { struct raw3270 *rp; struct raw3270_view *view, *tmp; @@ -1003,8 +972,7 @@ raw3270_find_view(struct raw3270_fn *fn, int minor) /* * Remove view from device and free view structure via call to view->fn->free. */ -void -raw3270_del_view(struct raw3270_view *view) +void raw3270_del_view(struct raw3270_view *view) { unsigned long flags; struct raw3270 *rp; @@ -1037,8 +1005,7 @@ raw3270_del_view(struct raw3270_view *view) /* * Remove a 3270 device structure. */ -static void -raw3270_delete_device(struct raw3270 *rp) +static void raw3270_delete_device(struct raw3270 *rp) { struct ccw_device *cdev; @@ -1061,8 +1028,7 @@ raw3270_delete_device(struct raw3270 *rp) kfree(rp); } -static int -raw3270_probe (struct ccw_device *cdev) +static int raw3270_probe(struct ccw_device *cdev) { return 0; } @@ -1070,16 +1036,16 @@ raw3270_probe (struct ccw_device *cdev) /* * Additional attributes for a 3270 device */ -static ssize_t -raw3270_model_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t raw3270_model_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->model); } static DEVICE_ATTR(model, 0444, raw3270_model_show, NULL); -static ssize_t -raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t raw3270_rows_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->rows); @@ -1087,7 +1053,8 @@ raw3270_rows_show(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(rows, 0444, raw3270_rows_show, NULL); static ssize_t -raw3270_columns_show(struct device *dev, struct device_attribute *attr, char *buf) +raw3270_columns_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->cols); @@ -1141,8 +1108,7 @@ void raw3270_unregister_notifier(struct raw3270_notifier *notifier) /* * Set 3270 device online. */ -static int -raw3270_set_online (struct ccw_device *cdev) +static int raw3270_set_online(struct ccw_device *cdev) { struct raw3270_notifier *np; struct raw3270 *rp; @@ -1169,8 +1135,7 @@ raw3270_set_online (struct ccw_device *cdev) /* * Remove 3270 device structure. */ -static void -raw3270_remove (struct ccw_device *cdev) +static void raw3270_remove(struct ccw_device *cdev) { unsigned long flags; struct raw3270 *rp; @@ -1220,8 +1185,7 @@ raw3270_remove (struct ccw_device *cdev) /* * Set 3270 device offline. */ -static int -raw3270_set_offline (struct ccw_device *cdev) +static int raw3270_set_offline(struct ccw_device *cdev) { struct raw3270 *rp; @@ -1260,8 +1224,7 @@ static struct ccw_driver raw3270_ccw_driver = { .int_class = IRQIO_C70, }; -static int -raw3270_init(void) +static int raw3270_init(void) { struct raw3270 *rp; int rc; @@ -1283,8 +1246,7 @@ raw3270_init(void) return rc; } -static void -raw3270_exit(void) +static void raw3270_exit(void) { ccw_driver_unregister(&raw3270_ccw_driver); class_destroy(class3270); From 815f3eeea9748213eb565395b2df5953969b18b1 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 29 Nov 2022 12:11:05 +0100 Subject: [PATCH 017/182] s390/tty3270: use switch/case in tty3270_erase_line() This makes the code easier to read. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 2ccfe14765e6..90671dd62cb3 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1306,9 +1306,11 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) int i; line = tp->screen + tp->cy; - if (mode == 0) + switch (mode) { + case 0: line->len = tp->cx; - else if (mode == 1) { + break; + case 1: for (i = 0; i < tp->cx; i++) { cell = line->cells + i; cell->character = ' '; @@ -1317,8 +1319,13 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) } if (line->len <= tp->cx) line->len = tp->cx + 1; - } else if (mode == 2) + break; + case 2: line->len = 0; + break; + default: + return; + } tty3270_convert_line(tp, tp->cy); } From 65b77ccb1e292d3d2407bbe225995d7c4c39e721 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 27 Nov 2022 09:26:24 +0100 Subject: [PATCH 018/182] s390/tty3270: use switch/case in tty3270_erase_display() This makes the code easier to read. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 90671dd62cb3..84e8e077c574 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1337,25 +1337,29 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) */ static void tty3270_erase_display(struct tty3270 *tp, int mode) { - int i; + int i, start, end; - if (mode == 0) { + switch (mode) { + case 0: tty3270_erase_line(tp, 0); - for (i = tp->cy + 1; i < tp->view.rows - 2; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); - } - } else if (mode == 1) { - for (i = 0; i < tp->cy; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); - } + start = tp->cy + 1; + end = tp->view.rows - 2; + break; + case 1: + start = 0; + end = tp->cy; tty3270_erase_line(tp, 1); - } else if (mode == 2) { - for (i = 0; i < tp->view.rows - 2; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); - } + break; + case 2: + start = 0; + end = tp->view.rows - 2; + break; + default: + return; + } + for (i = start; i < end; i++) { + tp->screen[i].len = 0; + tty3270_convert_line(tp, i); } tty3270_rebuild_update(tp); } From 562baff57754240236eb5103ed6062d5fca67268 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 29 Nov 2022 11:49:23 +0100 Subject: [PATCH 019/182] s390/raw3270: use __packed instead of __attribute__((packed)) Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index dfb4ec150797..f1a817cfe624 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -385,7 +385,7 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ char ymin; char xmax; char ymax; - } __attribute__ ((packed)) uab; + } __packed uab; struct { /* Alternate Usable Area Self-Defining Parameter */ char l; /* Length of this Self-Defining Parm */ char sdpid; /* 0x02 if Alternate Usable Area */ @@ -398,8 +398,8 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ int auayr; char awauai; char ahauai; - } __attribute__ ((packed)) aua; -} __attribute__ ((packed)); + } __packed aua; +} __packed; static void raw3270_size_device_vm(struct raw3270 *rp) { From c2e9375ecd67a0d4ff07e21e447a5bd4bec4da48 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 29 Nov 2022 08:02:01 +0100 Subject: [PATCH 020/182] s390/tty3270: add struct tty3270_attribute In preparation of background color and graphic escape support add a structure for attributes can be copied at once. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 113 ++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 84e8e077c574..0427515f8df3 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -41,10 +41,14 @@ static int tty3270_max_index; static struct tty3270 *condev; static struct raw3270_fn tty3270_fn; +struct tty3270_attribute { + unsigned char highlight; /* Blink/reverse/underscore */ + unsigned char f_color; /* Foreground color */ +}; + struct tty3270_cell { unsigned char character; - unsigned char highlight; - unsigned char f_color; + struct tty3270_attribute attributes; }; struct tty3270_line { @@ -80,8 +84,8 @@ struct tty3270 { /* Current tty screen. */ unsigned int cx, cy; /* Current output position. */ - unsigned int highlight; /* Blink/reverse/underscore */ - unsigned int f_color; /* Foreground color */ + struct tty3270_attribute attributes; + struct tty3270_attribute saved_attributes; struct tty3270_line *screen; unsigned int n_model, n_cols, n_rows; /* New model & size */ struct work_struct resize_work; @@ -101,7 +105,6 @@ struct tty3270 { int esc_state, esc_ques, esc_npar; int esc_par[ESCAPE_NPAR]; unsigned int saved_cx, saved_cy; - unsigned int saved_highlight, saved_f_color; /* Command recalling. */ struct list_head rcl_lines; /* List of recallable lines. */ @@ -1067,16 +1070,14 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) while (line->len < tp->cx) { cell = line->cells + line->len; cell->character = tp->view.ascebc[' ']; - cell->highlight = tp->highlight; - cell->f_color = tp->f_color; + cell->attributes = tp->attributes; line->len++; } line->len++; } cell = line->cells + tp->cx; cell->character = tp->view.ascebc[(unsigned int) ch]; - cell->highlight = tp->highlight; - cell->f_color = tp->f_color; + cell->attributes = tp->attributes; } /* @@ -1099,13 +1100,13 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) highlight = TAX_RESET; f_color = TAC_RESET; for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->highlight != highlight) { + if (cell->attributes.highlight != highlight) { flen += 3; /* TO_SA to switch highlight. */ - highlight = cell->highlight; + highlight = cell->attributes.highlight; } - if (cell->f_color != f_color) { + if (cell->attributes.f_color != f_color) { flen += 3; /* TO_SA to switch color. */ - f_color = cell->f_color; + f_color = cell->attributes.f_color; } } if (highlight != TAX_RESET) @@ -1143,17 +1144,17 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) highlight = TAX_RESET; f_color = TAC_RESET; for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->highlight != highlight) { + if (cell->attributes.highlight != highlight) { *cp++ = TO_SA; *cp++ = TAT_EXTHI; - *cp++ = cell->highlight; - highlight = cell->highlight; + *cp++ = cell->attributes.highlight; + highlight = cell->attributes.highlight; } - if (cell->f_color != f_color) { + if (cell->attributes.f_color != f_color) { *cp++ = TO_SA; *cp++ = TAT_COLOR; - *cp++ = cell->f_color; - f_color = cell->f_color; + *cp++ = cell->attributes.f_color; + f_color = cell->attributes.f_color; } *cp++ = cell->character; } @@ -1224,6 +1225,18 @@ static void tty3270_ri(struct tty3270 *tp) } } +static void tty3270_reset_attributes(struct tty3270_attribute *attr) +{ + attr->highlight = TAX_RESET; + attr->f_color = TAC_RESET; +} + +static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) +{ + cell->character = tp->view.ascebc[' ']; + tty3270_reset_attributes(&cell->attributes); +} + /* * Insert characters at current position. */ @@ -1233,12 +1246,8 @@ static void tty3270_insert_characters(struct tty3270 *tp, int n) int k; line = tp->screen + tp->cy; - while (line->len < tp->cx) { - line->cells[line->len].character = tp->view.ascebc[' ']; - line->cells[line->len].highlight = TAX_RESET; - line->cells[line->len].f_color = TAC_RESET; - line->len++; - } + while (line->len < tp->cx) + tty3270_reset_cell(tp, &line->cells[line->len++]); if (n > tp->view.cols - tp->cx) n = tp->view.cols - tp->cx; k = min_t(int, line->len - tp->cx, tp->view.cols - tp->cx - n); @@ -1249,8 +1258,7 @@ static void tty3270_insert_characters(struct tty3270 *tp, int n) line->len = tp->view.cols; while (n-- > 0) { line->cells[tp->cx + n].character = tp->view.ascebc[' ']; - line->cells[tp->cx + n].highlight = tp->highlight; - line->cells[tp->cx + n].f_color = tp->f_color; + line->cells[tp->cx + n].attributes = tp->attributes; } } @@ -1285,9 +1293,7 @@ static void tty3270_erase_characters(struct tty3270 *tp, int n) line = tp->screen + tp->cy; while (line->len > tp->cx && n-- > 0) { cell = line->cells + tp->cx++; - cell->character = ' '; - cell->highlight = TAX_RESET; - cell->f_color = TAC_RESET; + tty3270_reset_cell(tp, cell); } tp->cx += n; tp->cx = min_t(int, tp->cx, tp->view.cols - 1); @@ -1314,8 +1320,8 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) for (i = 0; i < tp->cx; i++) { cell = line->cells + i; cell->character = ' '; - cell->highlight = TAX_RESET; - cell->f_color = TAC_RESET; + cell->attributes.highlight = TAX_RESET; + cell->attributes.f_color = TAC_RESET; } if (line->len <= tp->cx) line->len = tp->cx + 1; @@ -1380,30 +1386,29 @@ static void tty3270_set_attributes(struct tty3270 *tp) attr = tp->esc_par[i]; switch (attr) { case 0: /* Reset */ - tp->highlight = TAX_RESET; - tp->f_color = TAC_RESET; + tty3270_reset_attributes(&tp->attributes); break; /* Highlight. */ case 4: /* Start underlining. */ - tp->highlight = TAX_UNDER; + tp->attributes.highlight = TAX_UNDER; break; case 5: /* Start blink. */ - tp->highlight = TAX_BLINK; + tp->attributes.highlight = TAX_BLINK; break; case 7: /* Start reverse. */ - tp->highlight = TAX_REVER; + tp->attributes.highlight = TAX_REVER; break; case 24: /* End underlining */ - if (tp->highlight == TAX_UNDER) - tp->highlight = TAX_RESET; + if (tp->attributes.highlight == TAX_UNDER) + tp->attributes.highlight = TAX_RESET; break; case 25: /* End blink. */ - if (tp->highlight == TAX_BLINK) - tp->highlight = TAX_RESET; + if (tp->attributes.highlight == TAX_BLINK) + tp->attributes.highlight = TAX_RESET; break; case 27: /* End reverse. */ - if (tp->highlight == TAX_REVER) - tp->highlight = TAX_RESET; + if (tp->attributes.highlight == TAX_REVER) + tp->attributes.highlight = TAX_RESET; break; /* Foreground color. */ case 30: /* Black */ @@ -1415,7 +1420,7 @@ static void tty3270_set_attributes(struct tty3270 *tp) case 36: /* Cyan */ case 37: /* White */ case 39: /* Black */ - tp->f_color = f_colors[attr - 30]; + tp->attributes.f_color = f_colors[attr - 30]; break; } } @@ -1491,20 +1496,18 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) case '7': /* Save cursor position. */ tp->saved_cx = tp->cx; tp->saved_cy = tp->cy; - tp->saved_highlight = tp->highlight; - tp->saved_f_color = tp->f_color; + tp->saved_attributes = tp->attributes; break; case '8': /* Restore cursor position. */ tty3270_convert_line(tp, tp->cy); tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); - tp->highlight = tp->saved_highlight; - tp->f_color = tp->saved_f_color; + tp->attributes = tp->saved_attributes; break; case 'c': /* Reset terminal. */ tp->cx = tp->saved_cx = 0; tp->cy = tp->saved_cy = 0; - tp->highlight = tp->saved_highlight = TAX_RESET; - tp->f_color = tp->saved_f_color = TAC_RESET; + tty3270_reset_attributes(&tp->attributes); + tty3270_reset_attributes(&tp->saved_attributes); tty3270_erase_display(tp, 2); break; } @@ -1592,14 +1595,12 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) case 's': /* Save cursor position. */ tp->saved_cx = tp->cx; tp->saved_cy = tp->cy; - tp->saved_highlight = tp->highlight; - tp->saved_f_color = tp->f_color; + tp->saved_attributes = tp->attributes; break; case 'u': /* Restore cursor position. */ tty3270_convert_line(tp, tp->cy); tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); - tp->highlight = tp->saved_highlight; - tp->f_color = tp->saved_f_color; + tp->attributes = tp->saved_attributes; break; } } @@ -1791,8 +1792,8 @@ static void tty3270_hangup(struct tty_struct *tty) spin_lock_irq(&tp->view.lock); tp->cx = tp->saved_cx = 0; tp->cy = tp->saved_cy = 0; - tp->highlight = tp->saved_highlight = TAX_RESET; - tp->f_color = tp->saved_f_color = TAC_RESET; + tty3270_reset_attributes(&tp->attributes); + tty3270_reset_attributes(&tp->saved_attributes); tty3270_blank_screen(tp); while (tp->nr_lines < tp->view.rows - 2) tty3270_blank_line(tp); From 4043ea22535d00325b469c75cf4840612c09345b Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 17 Nov 2022 22:22:15 +0100 Subject: [PATCH 021/182] s390/tty3270: add support for background color 3270 terminals support 8 background colors. Add the code to utilize them. Unfortunately the line erase code need to be adjusted: Without background colors, it was sufficient to just set the line length to zero. With background colors, we need to put spaces with the correct background color. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 103 +++++++++++++++++++++++++++--------- drivers/s390/char/raw3270.h | 3 +- 2 files changed, 80 insertions(+), 26 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 0427515f8df3..3b0e69f02773 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -42,8 +42,9 @@ static struct tty3270 *condev; static struct raw3270_fn tty3270_fn; struct tty3270_attribute { - unsigned char highlight; /* Blink/reverse/underscore */ - unsigned char f_color; /* Foreground color */ + unsigned char highlight; /* Blink/reverse/underscore */ + unsigned char f_color; /* Foreground color */ + unsigned char b_color; /* Background color */ }; struct tty3270_cell { @@ -205,10 +206,11 @@ static void tty3270_update_status(struct tty3270 *tp) static void tty3270_create_status(struct tty3270 *tp) { - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, TAC_GREEN, - 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_COLOR, - TAC_RESET }; + static const unsigned char blueprint[] = { + TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR, TAC_GREEN, + 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR, + TAC_RESET + }; struct string *line; unsigned int offset; @@ -302,9 +304,10 @@ static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size) */ static void tty3270_blank_line(struct tty3270 *tp) { - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET, - TO_SA, TAT_COLOR, TAC_RESET, TO_RA, 0, 0, 0 }; + static const unsigned char blueprint[] = { + TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET, + TO_SA, TAT_FGCOLOR, TAC_RESET, TO_RA, 0, 0, 0, + }; struct string *s; s = tty3270_alloc_string(tp, sizeof(blueprint)); @@ -1089,7 +1092,7 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) struct tty3270_cell *cell; struct string *s, *n; unsigned char highlight; - unsigned char f_color; + unsigned char f_color, b_color; char *cp; int flen, i; @@ -1099,6 +1102,7 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) flen += line->len; highlight = TAX_RESET; f_color = TAC_RESET; + b_color = TAC_RESET; for (i = 0, cell = line->cells; i < line->len; i++, cell++) { if (cell->attributes.highlight != highlight) { flen += 3; /* TO_SA to switch highlight. */ @@ -1108,11 +1112,17 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) flen += 3; /* TO_SA to switch color. */ f_color = cell->attributes.f_color; } + if (cell->attributes.b_color != b_color) { + flen += 3; /* TO_SA to switch color. */ + b_color = cell->attributes.b_color; + } } if (highlight != TAX_RESET) flen += 3; /* TO_SA to reset hightlight. */ if (f_color != TAC_RESET) flen += 3; /* TO_SA to reset color. */ + if (b_color != TAC_RESET) + flen += 3; /* TO_SA to reset color. */ if (line->len < tp->view.cols) flen += 4; /* Postfix (TO_RA). */ @@ -1143,6 +1153,7 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) highlight = TAX_RESET; f_color = TAC_RESET; + b_color = TAC_RESET; for (i = 0, cell = line->cells; i < line->len; i++, cell++) { if (cell->attributes.highlight != highlight) { *cp++ = TO_SA; @@ -1152,10 +1163,17 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) } if (cell->attributes.f_color != f_color) { *cp++ = TO_SA; - *cp++ = TAT_COLOR; + *cp++ = TAT_FGCOLOR; *cp++ = cell->attributes.f_color; f_color = cell->attributes.f_color; } + if (cell->attributes.b_color != b_color) { + *cp++ = TO_SA; + *cp++ = TAT_BGCOLOR; + *cp++ = cell->attributes.b_color; + b_color = cell->attributes.b_color; + } + *cp++ = cell->character; } if (highlight != TAX_RESET) { @@ -1165,7 +1183,12 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) } if (f_color != TAC_RESET) { *cp++ = TO_SA; - *cp++ = TAT_COLOR; + *cp++ = TAT_FGCOLOR; + *cp++ = TAC_RESET; + } + if (b_color != TAC_RESET) { + *cp++ = TO_SA; + *cp++ = TAT_BGCOLOR; *cp++ = TAC_RESET; } if (line->len < tp->view.cols) { @@ -1229,6 +1252,7 @@ static void tty3270_reset_attributes(struct tty3270_attribute *attr) { attr->highlight = TAX_RESET; attr->f_color = TAC_RESET; + attr->b_color = TAC_RESET; } static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) @@ -1309,29 +1333,36 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) { struct tty3270_line *line; struct tty3270_cell *cell; - int i; + int i, start, end; line = tp->screen + tp->cy; + switch (mode) { case 0: - line->len = tp->cx; + start = tp->cx; + end = tp->view.cols; break; case 1: - for (i = 0; i < tp->cx; i++) { - cell = line->cells + i; - cell->character = ' '; - cell->attributes.highlight = TAX_RESET; - cell->attributes.f_color = TAC_RESET; - } - if (line->len <= tp->cx) - line->len = tp->cx + 1; + start = 0; + end = tp->cx; break; case 2: - line->len = 0; + start = 0; + end = tp->view.cols; break; default: return; } + + for (i = start; i < end; i++) { + cell = line->cells + i; + tty3270_reset_cell(tp, cell); + cell->attributes.b_color = tp->attributes.b_color; + } + + if (line->len <= end) + line->len = end; + tty3270_convert_line(tp, tp->cy); } @@ -1376,7 +1407,7 @@ static void tty3270_erase_display(struct tty3270 *tp, int mode) */ static void tty3270_set_attributes(struct tty3270 *tp) { - static unsigned char f_colors[] = { + static unsigned char colors[] = { TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE, TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT }; @@ -1420,7 +1451,19 @@ static void tty3270_set_attributes(struct tty3270 *tp) case 36: /* Cyan */ case 37: /* White */ case 39: /* Black */ - tp->attributes.f_color = f_colors[attr - 30]; + tp->attributes.f_color = colors[attr - 30]; + break; + /* Background color. */ + case 40: /* Black */ + case 41: /* Red */ + case 42: /* Green */ + case 43: /* Yellow */ + case 44: /* Blue */ + case 45: /* Magenta */ + case 46: /* Cyan */ + case 47: /* White */ + case 49: /* Black */ + tp->attributes.b_color = colors[attr - 40]; break; } } @@ -1433,10 +1476,20 @@ static inline int tty3270_getpar(struct tty3270 *tp, int ix) static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) { + struct tty3270_line *line; + struct tty3270_cell *cell; int max_cx = max(0, cx); int max_cy = max(0, cy); tp->cx = min_t(int, tp->view.cols - 1, max_cx); + line = tp->screen + tp->cy; + while (line->len < tp->cx) { + cell = line->cells + line->len; + cell->character = ' '; + cell->attributes = tp->attributes; + line->len++; + } + cy = min_t(int, tp->view.rows - 3, max_cy); if (cy != tp->cy) { tty3270_convert_line(tp, tp->cy); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 4cb6b5ee44ca..7aa043e16622 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -48,8 +48,9 @@ #define TAT_RESET 0x00 #define TAT_FIELD 0xc0 #define TAT_EXTHI 0x41 -#define TAT_COLOR 0x42 +#define TAT_FGCOLOR 0x42 #define TAT_CHARS 0x43 +#define TAT_BGCOLOR 0x45 #define TAT_TRANS 0x46 /* Extended-Highlighting Bytes */ From 94dbb0a76ce21878867210d1cf0b21725023b452 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 22:22:01 +0100 Subject: [PATCH 022/182] s390/tty3270: add support for graphic escape Add support for ASCII S0/S1 to switch between character charset and graphic charset. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 44 ++++++++++++++++++++++++++++++++++++- drivers/s390/char/raw3270.h | 1 + 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 3b0e69f02773..13e2b4cb74fc 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -42,6 +42,7 @@ static struct tty3270 *condev; static struct raw3270_fn tty3270_fn; struct tty3270_attribute { + unsigned char alternate_charset:1; /* Graphics charset */ unsigned char highlight; /* Blink/reverse/underscore */ unsigned char f_color; /* Foreground color */ unsigned char b_color; /* Background color */ @@ -1059,6 +1060,36 @@ static unsigned int tty3270_write_room(struct tty_struct *tty) return INT_MAX; } +static char tty3270_graphics_translate(struct tty3270 *tp, char ch) +{ + switch (ch) { + case 'q': /* - */ + return 0xa2; + case 'x': /* '|' */ + return 0x85; + case 'l': /* |- */ + return 0xc5; + case 't': /* |_ */ + return 0xc6; + case 'u': /* _| */ + return 0xd6; + case 'k': /* -| */ + return 0xd5; + case 'j': + return 0xd4; + case 'm': + return 0xc4; + case 'n': /* + */ + return 0xd3; + case 'v': + return 0xc7; + case 'w': + return 0xd7; + default: + return ch; + } +} + /* * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. @@ -1079,7 +1110,10 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) line->len++; } cell = line->cells + tp->cx; - cell->character = tp->view.ascebc[(unsigned int) ch]; + if (tp->attributes.alternate_charset) + cell->character = tty3270_graphics_translate(tp, ch); + else + cell->character = tp->view.ascebc[(unsigned int)ch]; cell->attributes = tp->attributes; } @@ -1116,6 +1150,8 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) flen += 3; /* TO_SA to switch color. */ b_color = cell->attributes.b_color; } + if (cell->attributes.alternate_charset) + flen += 1; /* TO_GE to switch to graphics extensions */ } if (highlight != TAX_RESET) flen += 3; /* TO_SA to reset hightlight. */ @@ -1173,6 +1209,8 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) *cp++ = cell->attributes.b_color; b_color = cell->attributes.b_color; } + if (cell->attributes.alternate_charset) + *cp++ = TO_GE; *cp++ = cell->character; } @@ -1706,7 +1744,11 @@ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, case 0x0d: /* '\r' -- Carriage Return */ tp->cx = 0; break; + case 0x0e: + tp->attributes.alternate_charset = 1; + break; case 0x0f: /* SuSE "exit alternate mode" */ + tp->attributes.alternate_charset = 0; break; case 0x1b: /* Start escape sequence. */ tty3270_escape_sequence(tp, buf[i_msg]); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 7aa043e16622..cdf03677118d 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -28,6 +28,7 @@ #define TC_WRITESF 0x11 /* Write structured field */ /* Buffer Control Orders */ +#define TO_GE 0x08 /* Graphics Escape */ #define TO_SF 0x1d /* Start field */ #define TO_SBA 0x11 /* Set buffer address */ #define TO_IC 0x13 /* Insert cursor */ From e4b57b93935d103aae10abea361af77ef906f368 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 17 Nov 2022 17:30:40 +0100 Subject: [PATCH 023/182] s390/tty3270: add support for VT100 graphics escape Add support for ESC(B and ESC(0 to switch between character charset and graphics charset. Used in vt100 and later terminal generations. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 13e2b4cb74fc..9c816d1239eb 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1557,7 +1557,7 @@ static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) */ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) { - enum { ESnormal, ESesc, ESsquare, ESgetpars }; + enum { ESnormal, ESesc, ESsquare, ESparen, ESgetpars }; if (tp->esc_state == ESnormal) { if (ch == 0x1b) @@ -1571,6 +1571,9 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) case '[': tp->esc_state = ESsquare; break; + case '(': + tp->esc_state = ESparen; + break; case 'E': tty3270_cr(tp); tty3270_lf(tp); @@ -1604,15 +1607,28 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) } return; } - if (tp->esc_state == ESsquare) { + + switch (tp->esc_state) { + case ESparen: + tp->esc_state = ESnormal; + switch (ch) { + case 'B': + tp->attributes.alternate_charset = 0; + break; + case '0': + tp->attributes.alternate_charset = 1; + break; + } + return; + case ESsquare: tp->esc_state = ESgetpars; memset(tp->esc_par, 0, sizeof(tp->esc_par)); tp->esc_npar = 0; tp->esc_ques = (ch == '?'); if (tp->esc_ques) return; - } - if (tp->esc_state == ESgetpars) { + fallthrough; + case ESgetpars: if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) { tp->esc_npar++; return; @@ -1622,6 +1638,9 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) tp->esc_par[tp->esc_npar] += ch - '0'; return; } + break; + default: + break; } tp->esc_state = ESnormal; if (ch == 'n' && !tp->esc_ques) { From 970cf9a97a27d3f9a72a17aa6aedb47758478c33 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 17 Nov 2022 17:52:40 +0100 Subject: [PATCH 024/182] s390/tty3270: ignore NUL characters With 'TERM=vt220' zsh is sending several NUL characters with the prompt to the tty. Both xterm and the linux drm console seem to ignore them. Ignore them in tty3270 as well. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 9c816d1239eb..7ff116c38715 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1732,6 +1732,8 @@ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, } switch (buf[i_msg]) { + case 0x00: + break; case 0x07: /* '\a' -- Alarm */ tp->wcc |= TW_PLUSALARM; break; From e22de7d7910ab9490b191da5fac0592214b340d5 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:40:44 +0100 Subject: [PATCH 025/182] s390/tty3270: add AID defines Use AID_* instead of hex numbers to make the code a bit easier to read. also convert the if/else blocks to a switch statement in tty3270_read_tasklet(). Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 8 ++++++-- drivers/s390/char/raw3270.h | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 7ff116c38715..0dea178c0f56 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -565,7 +565,8 @@ static void tty3270_read_tasklet(unsigned long data) */ input = NULL; len = 0; - if (tp->input->string[0] == 0x7d) { + switch (tp->input->string[0]) { + case AID_ENTER: /* Enter: write input to tty. */ input = tp->input->string + 6; len = tp->input->len - 6 - rrq->rescnt; @@ -579,10 +580,13 @@ static void tty3270_read_tasklet(unsigned long data) /* Clear input area. */ tty3270_update_prompt(tp, NULL, 0); tty3270_set_timer(tp, 1); - } else if (tp->input->string[0] == 0x6d) { + break; + case AID_CLEAR: /* Display has been cleared. Redraw. */ tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); + default: + break; } spin_unlock_irq(&tp->view.lock); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index cdf03677118d..7e0c88e9c3c2 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -82,6 +82,13 @@ #define RAW3270_FIRSTMINOR 1 /* First minor number */ #define RAW3270_MAXDEVS 255 /* Max number of 3270 devices */ +#define AID_CLEAR 0x6d +#define AID_ENTER 0x7d +#define AID_PF3 0xf3 +#define AID_PF7 0xf7 +#define AID_PF8 0xf8 +#define AID_READ_PARTITION 0x88 + /* For TUBGETMOD and TUBSETMOD. Should include. */ struct raw3270_iocb { short model; From f08e31558a98383f185dcff0a8d77f1963150156 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:42:28 +0100 Subject: [PATCH 026/182] s390/raw3270: add raw3270_start_request() helper There are a few places (and there would be more with the following commits) like this: raw3270_request_reset(cp->kreset); raw3270_request_set_cmd(cp->kreset, TC_WRITE); raw3270_request_add_data(cp->kreset, &kreset_data, 1); raw3270_start(&cp->view, cp->kreset); i.e reset a request, setting the command, adding payload, and starting the request. Add a helper raw3270_start_request() which takes a command and the payload as argument and calls the approppriate functions. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 5 +---- drivers/s390/char/raw3270.c | 14 ++++++++++++++ drivers/s390/char/raw3270.h | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 0dea178c0f56..88e96957cbb2 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -591,10 +591,7 @@ static void tty3270_read_tasklet(unsigned long data) spin_unlock_irq(&tp->view.lock); /* Start keyboard reset command. */ - raw3270_request_reset(tp->kreset); - raw3270_request_set_cmd(tp->kreset, TC_WRITE); - raw3270_request_add_data(tp->kreset, &kreset_data, 1); - raw3270_start(&tp->view, tp->kreset); + raw3270_start_request(&tp->view, tp->kreset, TC_WRITE, &kreset_data, 1); while (len-- > 0) kbd_keycode(tp->kbd, *input++); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index f1a817cfe624..68a6c7390c4c 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -264,6 +264,19 @@ int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) return rc; } +int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, + int cmd, void *data, size_t len) +{ + int rc; + + raw3270_request_reset(rq); + raw3270_request_set_cmd(rq, cmd); + rc = raw3270_request_add_data(rq, data, len); + if (rc) + return rc; + return raw3270_start(view, rq); +} + int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) { struct raw3270 *rp; @@ -1272,6 +1285,7 @@ EXPORT_SYMBOL(raw3270_find_view); EXPORT_SYMBOL(raw3270_activate_view); EXPORT_SYMBOL(raw3270_deactivate_view); EXPORT_SYMBOL(raw3270_start); +EXPORT_SYMBOL(raw3270_start_request); EXPORT_SYMBOL(raw3270_start_locked); EXPORT_SYMBOL(raw3270_start_irq); EXPORT_SYMBOL(raw3270_reset); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 7e0c88e9c3c2..c8e7a596051f 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -180,6 +180,8 @@ int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *); int raw3270_reset(struct raw3270_view *); struct raw3270_view *raw3270_view(struct raw3270_view *); int raw3270_view_active(struct raw3270_view *); +int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, + int cmd, void *data, size_t len); /* Reference count inliner for view structures. */ static inline void From 91621ba7d7b7274cd44e5ee4942a39a6aae977a0 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:50:10 +0100 Subject: [PATCH 027/182] s390/tty3270: move resize work to raw3270 This change was initially made to reduce code duplication when the con3270 and tty3270 shared the same resize code. It still makes sense to move the resize workqueue to raw3270 in case we add some other view later. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 46 +++++++++++++++---------------------- drivers/s390/char/raw3270.c | 28 +++++++++++++++++----- drivers/s390/char/raw3270.h | 2 +- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 88e96957cbb2..caccf5d496fd 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -89,8 +89,6 @@ struct tty3270 { struct tty3270_attribute attributes; struct tty3270_attribute saved_attributes; struct tty3270_line *screen; - unsigned int n_model, n_cols, n_rows; /* New model & size */ - struct work_struct resize_work; /* Input stuff. */ struct string *prompt; /* Output string for input area. */ @@ -126,8 +124,6 @@ struct tty3270 { #define TTY_UPDATE_ALL 16 /* Recreate screen. */ static void tty3270_update(struct timer_list *); -static void tty3270_resize_work(struct work_struct *work); - /* * Setup timeout for a device. On timeout trigger an update. */ @@ -745,8 +741,6 @@ static struct tty3270 *tty3270_alloc_view(void) (unsigned long) tp->read); tasklet_init(&tp->hanglet, tty3270_hangup_tasklet, (unsigned long) tp); - INIT_WORK(&tp->resize_work, tty3270_resize_work); - return tp; out_reset: @@ -827,26 +821,36 @@ static void tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) /* * Resize tty3270 screen */ -static void tty3270_resize_work(struct work_struct *work) +static void tty3270_resize(struct raw3270_view *view, + int new_model, int new_rows, int new_cols, + int old_model, int old_rows, int old_cols) { - struct tty3270 *tp = container_of(work, struct tty3270, resize_work); + struct tty3270 *tp = container_of(view, struct tty3270, view); struct tty3270_line *screen, *oscreen; struct tty_struct *tty; - unsigned int orows; struct winsize ws; - screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols); + if (old_model == new_model && + old_cols == new_cols && + old_rows == new_rows) { + spin_lock_irq(&tp->view.lock); + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); + spin_unlock_irq(&tp->view.lock); + return; + } + screen = tty3270_alloc_screen(new_rows, new_cols); if (IS_ERR(screen)) return; /* Switch to new output size */ spin_lock_irq(&tp->view.lock); tty3270_blank_screen(tp); oscreen = tp->screen; - orows = tp->view.rows; - tp->view.model = tp->n_model; - tp->view.rows = tp->n_rows; - tp->view.cols = tp->n_cols; tp->screen = screen; + tp->view.rows = new_rows; + tp->view.cols = new_cols; + tp->view.model = new_model; + free_string(&tp->freemem, tp->prompt); free_string(&tp->freemem, tp->status); tty3270_create_prompt(tp); @@ -855,7 +859,7 @@ static void tty3270_resize_work(struct work_struct *work) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; spin_unlock_irq(&tp->view.lock); - tty3270_free_screen(oscreen, orows); + tty3270_free_screen(oscreen, old_rows); tty3270_set_timer(tp, 1); /* Informat tty layer about new size */ tty = tty_port_tty_get(&tp->port); @@ -867,18 +871,6 @@ static void tty3270_resize_work(struct work_struct *work) tty_kref_put(tty); } -static void tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) -{ - struct tty3270 *tp = container_of(view, struct tty3270, view); - - if (tp->n_model == model && tp->n_rows == rows && tp->n_cols == cols) - return; - tp->n_model = model; - tp->n_rows = rows; - tp->n_cols = cols; - schedule_work(&tp->resize_work); -} - /* * Unlink tty3270 data structure from tty. */ diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 68a6c7390c4c..032244c1dd78 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -37,7 +37,8 @@ struct raw3270 { struct ccw_device *cdev; int minor; - short model, rows, cols; + int model, rows, cols; + int old_model, old_rows, old_cols; unsigned int state; unsigned long flags; @@ -54,6 +55,7 @@ struct raw3270 { struct raw3270_request init_readpart; struct raw3270_request init_readmod; unsigned char init_data[256]; + struct work_struct resize_work; }; /* raw3270->state */ @@ -502,16 +504,20 @@ static void raw3270_size_device(struct raw3270 *rp) rp->model = 5; } -static void raw3270_size_device_done(struct raw3270 *rp) +static void raw3270_resize_work(struct work_struct *work) { + struct raw3270 *rp = container_of(work, struct raw3270, resize_work); struct raw3270_view *view; - rp->view = NULL; - rp->state = RAW3270_STATE_READY; /* Notify views about new size */ - list_for_each_entry(view, &rp->view_list, list) + list_for_each_entry(view, &rp->view_list, list) { if (view->fn->resize) - view->fn->resize(view, rp->model, rp->rows, rp->cols); + view->fn->resize(view, rp->model, rp->rows, rp->cols, + rp->old_model, rp->old_rows, rp->old_cols); + } + rp->old_cols = rp->cols; + rp->old_rows = rp->rows; + rp->old_model = rp->model; /* Setup processing done, now activate a view */ list_for_each_entry(view, &rp->view_list, list) { rp->view = view; @@ -521,6 +527,13 @@ static void raw3270_size_device_done(struct raw3270 *rp) } } +static void raw3270_size_device_done(struct raw3270 *rp) +{ + rp->view = NULL; + rp->state = RAW3270_STATE_READY; + schedule_work(&rp->resize_work); +} + static void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; @@ -697,6 +710,8 @@ static int raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, /* Set defaults. */ rp->rows = 24; rp->cols = 80; + rp->old_rows = rp->rows; + rp->old_cols = rp->cols; INIT_LIST_HEAD(&rp->req_queue); INIT_LIST_HEAD(&rp->view_list); @@ -704,6 +719,7 @@ static int raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, rp->init_view.dev = rp; rp->init_view.fn = &raw3270_init_fn; rp->view = &rp->init_view; + INIT_WORK(&rp->resize_work, raw3270_resize_work); /* * Add device to list and find the smallest unused minor diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index c8e7a596051f..66f9f8d0e121 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -144,7 +144,7 @@ struct raw3270_fn { struct raw3270_request *, struct irb *); void (*release)(struct raw3270_view *); void (*free)(struct raw3270_view *); - void (*resize)(struct raw3270_view *, int, int, int); + void (*resize)(struct raw3270_view *, int, int, int, int, int, int); }; /* From cbb36313bdb696cfe0874406327b24b403c9e8e0 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:53:04 +0100 Subject: [PATCH 028/182] s390/tty3270: resize terminal when the clear key is pressed There's no easy way to figure out whether the user has re-connected to the z/VM session. When the user re-connected with a different geometry to z/VM, the screen layout is broken. Allow the user to force a resizing by pressing the Clear Key. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 20 +++++++++++++++++++- drivers/s390/char/raw3270.c | 14 +++++++------- drivers/s390/char/raw3270.h | 1 + 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index caccf5d496fd..61c73eb2471f 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -58,6 +58,10 @@ struct tty3270_line { int len; }; +static const unsigned char sfq_read_partition[] = { + 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 +}; + #define ESCAPE_NPAR 8 /* @@ -95,6 +99,7 @@ struct tty3270 { struct string *input; /* Input string for read request. */ struct raw3270_request *read; /* Single read request. */ struct raw3270_request *kreset; /* Single keyboard reset request. */ + struct raw3270_request *readpartreq; unsigned char inattr; /* Visible/invisible input. */ int throttle, attn; /* tty throttle/unthrottle. */ struct tasklet_struct readlet; /* Tasklet to issue read request. */ @@ -581,6 +586,14 @@ static void tty3270_read_tasklet(unsigned long data) /* Display has been cleared. Redraw. */ tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); + if (!list_empty(&tp->readpartreq->list)) + break; + raw3270_start_request(&tp->view, tp->readpartreq, TC_WRITESF, + (char *)sfq_read_partition, sizeof(sfq_read_partition)); + break; + case AID_READ_PARTITION: + raw3270_read_modified_cb(tp->readpartreq, tp->input->string); + break; default: break; } @@ -731,9 +744,12 @@ static struct tty3270 *tty3270_alloc_view(void) tp->kreset = raw3270_request_alloc(1); if (IS_ERR(tp->kreset)) goto out_read; + tp->readpartreq = raw3270_request_alloc(sizeof(sfq_read_partition)); + if (IS_ERR(tp->readpartreq)) + goto out_reset; tp->kbd = kbd_alloc(); if (!tp->kbd) - goto out_reset; + goto out_readpartreq; tty_port_init(&tp->port); timer_setup(&tp->timer, tty3270_update, 0); @@ -743,6 +759,8 @@ static struct tty3270 *tty3270_alloc_view(void) (unsigned long) tp); return tp; +out_readpartreq: + raw3270_request_free(tp->readpartreq); out_reset: raw3270_request_free(tp->kreset); out_read: diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 032244c1dd78..e2d703e8ad48 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -465,15 +465,14 @@ static void raw3270_size_device_vm(struct raw3270 *rp) } } -static void raw3270_size_device(struct raw3270 *rp) +static void raw3270_size_device(struct raw3270 *rp, char *init_data) { struct raw3270_ua *uap; /* Got a Query Reply */ - uap = (struct raw3270_ua *) (rp->init_data + 1); + uap = (struct raw3270_ua *)(init_data + 1); /* Paranoia check. */ - if (rp->init_readmod.rc || rp->init_data[0] != 0x88 || - uap->uab.qcode != 0x81) { + if (init_data[0] != 0x88 || uap->uab.qcode != 0x81) { /* Couldn't detect size. Use default model 2. */ rp->model = 2; rp->rows = 24; @@ -534,11 +533,10 @@ static void raw3270_size_device_done(struct raw3270 *rp) schedule_work(&rp->resize_work); } -static void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) +void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; - - raw3270_size_device(rp); + raw3270_size_device(rp, data); raw3270_size_device_done(rp); } @@ -554,6 +552,7 @@ static void raw3270_read_modified(struct raw3270 *rp) rp->init_readmod.ccw.count = sizeof(rp->init_data); rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data); rp->init_readmod.callback = raw3270_read_modified_cb; + rp->init_readmod.callback_data = rp->init_data; rp->state = RAW3270_STATE_READMOD; raw3270_start_irq(&rp->init_view, &rp->init_readmod); } @@ -1287,6 +1286,7 @@ module_init(raw3270_init); module_exit(raw3270_exit); EXPORT_SYMBOL(class3270); +EXPORT_SYMBOL(raw3270_read_modified_cb); EXPORT_SYMBOL(raw3270_request_alloc); EXPORT_SYMBOL(raw3270_request_free); EXPORT_SYMBOL(raw3270_request_reset); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 66f9f8d0e121..05cd501478ee 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -182,6 +182,7 @@ struct raw3270_view *raw3270_view(struct raw3270_view *); int raw3270_view_active(struct raw3270_view *); int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, int cmd, void *data, size_t len); +void raw3270_read_modified_cb(struct raw3270_request *rq, void *data); /* Reference count inliner for view structures. */ static inline void From 1fefd62fee50d4455ef5ebf65e22b282d773ed7d Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 19:55:57 +0100 Subject: [PATCH 029/182] s390/tty3270: split up tty3270_convert_line() To make the code easier to read, split up tty3270_convertline() into several subfunctions: - tty3270_resize_line() to realloc the line if it doesn't have enough space left - tty3270_required_length() to calculate how much space we need - tty3270_add_attributes() to add the color and highlight attributes - tty3270_add_reset_attributes() to reset the attributes at the end of the line Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 169 +++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 72 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 61c73eb2471f..f4e7ac406511 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1101,6 +1101,18 @@ static char tty3270_graphics_translate(struct tty3270 *tp, char ch) } } +static struct string *tty3270_resize_line(struct tty3270 *tp, struct string *s, int newlen) +{ + struct string *n = tty3270_alloc_string(tp, newlen); + + list_add(&n->list, &s->list); + list_del_init(&s->list); + if (!list_empty(&s->update)) + list_del_init(&s->update); + free_string(&tp->freemem, s); + return n; +} + /* * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. @@ -1128,26 +1140,19 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) cell->attributes = tp->attributes; } -/* - * Convert a tty3270_line to a 3270 data fragment usable for output. - */ -static void tty3270_convert_line(struct tty3270 *tp, int line_nr) +static int tty3270_required_length(struct tty3270 *tp, int line_nr) { + unsigned char f_color, b_color, highlight; struct tty3270_line *line; struct tty3270_cell *cell; - struct string *s, *n; - unsigned char highlight; - unsigned char f_color, b_color; - char *cp; - int flen, i; + int i, flen = 3; /* Prefix (TO_SBA). */ - /* Determine how long the fragment will be. */ - flen = 3; /* Prefix (TO_SBA). */ line = tp->screen + line_nr; flen += line->len; highlight = TAX_RESET; f_color = TAC_RESET; b_color = TAC_RESET; + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { if (cell->attributes.highlight != highlight) { flen += 3; /* TO_SA to switch highlight. */ @@ -1173,69 +1178,23 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) if (line->len < tp->view.cols) flen += 4; /* Postfix (TO_RA). */ - /* Find the line in the list. */ - i = tp->view.rows - 2 - line_nr; - list_for_each_entry_reverse(s, &tp->lines, list) - if (--i <= 0) - break; - /* - * Check if the line needs to get reallocated. - */ - if (s->len != flen) { - /* Reallocate string. */ - n = tty3270_alloc_string(tp, flen); - list_add(&n->list, &s->list); - list_del_init(&s->list); - if (!list_empty(&s->update)) - list_del_init(&s->update); - free_string(&tp->freemem, s); - s = n; - } + return flen; +} - /* Write 3270 data fragment. */ - cp = s->string; - *cp++ = TO_SBA; - *cp++ = 0; - *cp++ = 0; - - highlight = TAX_RESET; - f_color = TAC_RESET; - b_color = TAC_RESET; - for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->attributes.highlight != highlight) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = cell->attributes.highlight; - highlight = cell->attributes.highlight; - } - if (cell->attributes.f_color != f_color) { - *cp++ = TO_SA; - *cp++ = TAT_FGCOLOR; - *cp++ = cell->attributes.f_color; - f_color = cell->attributes.f_color; - } - if (cell->attributes.b_color != b_color) { - *cp++ = TO_SA; - *cp++ = TAT_BGCOLOR; - *cp++ = cell->attributes.b_color; - b_color = cell->attributes.b_color; - } - if (cell->attributes.alternate_charset) - *cp++ = TO_GE; - - *cp++ = cell->character; - } - if (highlight != TAX_RESET) { +static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, + char *cp, struct tty3270_attribute *attr) +{ + if (attr->highlight != TAX_RESET) { *cp++ = TO_SA; *cp++ = TAT_EXTHI; *cp++ = TAX_RESET; } - if (f_color != TAC_RESET) { + if (attr->f_color != TAC_RESET) { *cp++ = TO_SA; *cp++ = TAT_FGCOLOR; *cp++ = TAC_RESET; } - if (b_color != TAC_RESET) { + if (attr->b_color != TAC_RESET) { *cp++ = TO_SA; *cp++ = TAT_BGCOLOR; *cp++ = TAC_RESET; @@ -1246,7 +1205,80 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) *cp++ = 0; *cp++ = 0; } + return cp; +} +static char *tty3270_add_attributes(struct tty3270_line *line, struct tty3270_attribute *attr, + char *cp) +{ + struct tty3270_cell *cell; + int i; + + *cp++ = TO_SBA; + *cp++ = 0; + *cp++ = 0; + + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->attributes.highlight != attr->highlight) { + *cp++ = TO_SA; + *cp++ = TAT_EXTHI; + *cp++ = cell->attributes.highlight; + attr->highlight = cell->attributes.highlight; + } + if (cell->attributes.f_color != attr->f_color) { + *cp++ = TO_SA; + *cp++ = TAT_FGCOLOR; + *cp++ = cell->attributes.f_color; + attr->f_color = cell->attributes.f_color; + } + if (cell->attributes.b_color != attr->b_color) { + *cp++ = TO_SA; + *cp++ = TAT_BGCOLOR; + *cp++ = cell->attributes.b_color; + attr->b_color = cell->attributes.b_color; + } + if (cell->attributes.alternate_charset) + *cp++ = TO_GE; + *cp++ = cell->character; + } + return cp; +} + +static void tty3270_reset_attributes(struct tty3270_attribute *attr) +{ + attr->highlight = TAX_RESET; + attr->f_color = TAC_RESET; + attr->b_color = TAC_RESET; +} + +/* + * Convert a tty3270_line to a 3270 data fragment usable for output. + */ +static void tty3270_convert_line(struct tty3270 *tp, int line_nr) +{ + struct tty3270_line *line = tp->screen + line_nr; + struct tty3270_attribute attr; + struct string *s; + int flen, i; + char *cp; + + /* Determine how long the fragment will be. */ + flen = tty3270_required_length(tp, line_nr); + /* Find the line in the list. */ + i = tp->view.rows - 2 - line_nr; + list_for_each_entry_reverse(s, &tp->lines, list) + if (--i <= 0) + break; + /* + * Check if the line needs to get reallocated. + */ + if (s->len != flen) + s = tty3270_resize_line(tp, s, flen); + + /* Write 3270 data fragment. */ + tty3270_reset_attributes(&attr); + cp = tty3270_add_attributes(line, &attr, s->string); + cp = tty3270_add_reset_attributes(tp, line, cp, &attr); if (tp->nr_up + line_nr < tp->view.rows - 2) { /* Line is currently visible on screen. */ tty3270_update_string(tp, s, line_nr); @@ -1297,13 +1329,6 @@ static void tty3270_ri(struct tty3270 *tp) } } -static void tty3270_reset_attributes(struct tty3270_attribute *attr) -{ - attr->highlight = TAX_RESET; - attr->f_color = TAC_RESET; - attr->b_color = TAC_RESET; -} - static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) { cell->character = tp->view.ascebc[' ']; From 9eb99b941ba78a0aad996a3c129ee2a19507d87e Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 24 Nov 2022 20:32:20 +0100 Subject: [PATCH 030/182] s390/con3270: add helper to get number of tty rows There a quite a few places using 'tp->view.rows - 2'. Add a helper function for this. This will also be used when a function key help line will be added. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 52 +++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index f4e7ac406511..1c1390249342 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -128,6 +128,7 @@ struct tty3270 { #define TTY_UPDATE_STATUS 8 /* Update status line. */ #define TTY_UPDATE_ALL 16 /* Recreate screen. */ +#define TTY3270_INPUT_AREA_ROWS 2 static void tty3270_update(struct timer_list *); /* * Setup timeout for a device. On timeout trigger an update. @@ -137,6 +138,11 @@ static void tty3270_set_timer(struct tty3270 *tp, int expires) mod_timer(&tp->timer, jiffies + expires); } +static int tty3270_tty_rows(struct tty3270 *tp) +{ + return tp->view.rows - TTY3270_INPUT_AREA_ROWS; +} + /* * The input line are the two last lines of the screen. */ @@ -183,7 +189,7 @@ static void tty3270_create_prompt(struct tty3270 *tp) memcpy(line->string, blueprint, sizeof(blueprint)); line->len = sizeof(blueprint); /* Set output offsets. */ - offset = tp->view.cols * (tp->view.rows - 2); + offset = tp->view.cols * tty3270_tty_rows(tp); raw3270_buffer_address(tp->view.dev, line->string + 1, offset); offset = tp->view.cols * tp->view.rows - 9; raw3270_buffer_address(tp->view.dev, line->string + 8, offset); @@ -255,7 +261,7 @@ static void tty3270_rebuild_update(struct tty3270 *tp) */ list_for_each_entry_safe(s, n, &tp->update, update) list_del_init(&s->update); - line = tp->view.rows - 3; + line = tty3270_tty_rows(tp) - 1; nr_up = tp->nr_up; list_for_each_entry_reverse(s, &tp->lines, list) { if (nr_up > 0) { @@ -282,7 +288,7 @@ static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size) if (s) return s; list_for_each_entry_safe(s, n, &tp->lines, list) { - BUG_ON(tp->nr_lines <= tp->view.rows - 2); + BUG_ON(tp->nr_lines <= tty3270_tty_rows(tp)); list_del(&s->list); if (!list_empty(&s->update)) list_del(&s->update); @@ -293,8 +299,8 @@ static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size) s = alloc_string(&tp->freemem, size); BUG_ON(!s); if (tp->nr_up != 0 && - tp->nr_up + tp->view.rows - 2 >= tp->nr_lines) { - tp->nr_up = tp->nr_lines - tp->view.rows + 2; + tp->nr_up + tty3270_tty_rows(tp) >= tp->nr_lines) { + tp->nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS; tty3270_rebuild_update(tp); tty3270_update_status(tp); } @@ -329,7 +335,7 @@ static void tty3270_blank_screen(struct tty3270 *tp) struct string *s, *n; int i; - for (i = 0; i < tp->view.rows - 2; i++) + for (i = 0; i < tty3270_tty_rows(tp); i++) tp->screen[i].len = 0; tp->nr_up = 0; list_for_each_entry_safe(s, n, &tp->lines, list) { @@ -514,7 +520,7 @@ static void tty3270_scroll_forward(struct kbd_data *kbd) int nr_up; spin_lock_irq(&tp->view.lock); - nr_up = tp->nr_up - tp->view.rows + 2; + nr_up = tp->nr_up - tp->view.rows + TTY3270_INPUT_AREA_ROWS; if (nr_up < 0) nr_up = 0; if (nr_up != tp->nr_up) { @@ -535,9 +541,9 @@ static void tty3270_scroll_backward(struct kbd_data *kbd) int nr_up; spin_lock_irq(&tp->view.lock); - nr_up = tp->nr_up + tp->view.rows - 2; - if (nr_up + tp->view.rows - 2 > tp->nr_lines) - nr_up = tp->nr_lines - tp->view.rows + 2; + nr_up = tp->nr_up + tty3270_tty_rows(tp); + if (nr_up + tty3270_tty_rows(tp) > tp->nr_lines) + nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS; if (nr_up != tp->nr_up) { tp->nr_up = nr_up; tty3270_rebuild_update(tp); @@ -873,7 +879,7 @@ static void tty3270_resize(struct raw3270_view *view, free_string(&tp->freemem, tp->status); tty3270_create_prompt(tp); tty3270_create_status(tp); - while (tp->nr_lines < tp->view.rows - 2) + while (tp->nr_lines < tty3270_tty_rows(tp)) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; spin_unlock_irq(&tp->view.lock); @@ -883,7 +889,7 @@ static void tty3270_resize(struct raw3270_view *view, tty = tty_port_tty_get(&tp->port); if (!tty) return; - ws.ws_row = tp->view.rows - 2; + ws.ws_row = tty3270_tty_rows(tp); ws.ws_col = tp->view.cols; tty_do_resize(tty, &ws); tty_kref_put(tty); @@ -977,7 +983,7 @@ tty3270_create_view(int index, struct tty3270 **newtp) tty3270_update_status(tp); /* Create blank line for every line in the tty output area. */ - for (i = 0; i < tp->view.rows - 2; i++) + for (i = 0; i < tty3270_tty_rows(tp); i++) tty3270_blank_line(tp); tp->kbd->port = &tp->port; @@ -1015,7 +1021,7 @@ tty3270_install(struct tty_driver *driver, struct tty_struct *tty) tp->inattr = TF_INPUT; } - tty->winsize.ws_row = tp->view.rows - 2; + tty->winsize.ws_row = tty3270_tty_rows(tp); tty->winsize.ws_col = tp->view.cols; rc = tty_port_install(&tp->port, driver, tty); if (rc) { @@ -1265,7 +1271,7 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) /* Determine how long the fragment will be. */ flen = tty3270_required_length(tp, line_nr); /* Find the line in the list. */ - i = tp->view.rows - 2 - line_nr; + i = tty3270_tty_rows(tp) - line_nr; list_for_each_entry_reverse(s, &tp->lines, list) if (--i <= 0) break; @@ -1279,7 +1285,7 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) tty3270_reset_attributes(&attr); cp = tty3270_add_attributes(line, &attr, s->string); cp = tty3270_add_reset_attributes(tp, line, cp, &attr); - if (tp->nr_up + line_nr < tp->view.rows - 2) { + if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) { /* Line is currently visible on screen. */ tty3270_update_string(tp, s, line_nr); /* Add line to update list. */ @@ -1307,7 +1313,7 @@ static void tty3270_lf(struct tty3270 *tp) int i; tty3270_convert_line(tp, tp->cy); - if (tp->cy < tp->view.rows - 3) { + if (tp->cy < tty3270_tty_rows(tp) - 1) { tp->cy++; return; } @@ -1315,9 +1321,9 @@ static void tty3270_lf(struct tty3270 *tp) tty3270_blank_line(tp); temp = tp->screen[0]; temp.len = 0; - for (i = 0; i < tp->view.rows - 3; i++) + for (i = 0; i < tty3270_tty_rows(tp) - 1; i++) tp->screen[i] = tp->screen[i+1]; - tp->screen[tp->view.rows - 3] = temp; + tp->screen[tty3270_tty_rows(tp) - 1] = temp; tty3270_rebuild_update(tp); } @@ -1454,7 +1460,7 @@ static void tty3270_erase_display(struct tty3270 *tp, int mode) case 0: tty3270_erase_line(tp, 0); start = tp->cy + 1; - end = tp->view.rows - 2; + end = tty3270_tty_rows(tp); break; case 1: start = 0; @@ -1463,7 +1469,7 @@ static void tty3270_erase_display(struct tty3270 *tp, int mode) break; case 2: start = 0; - end = tp->view.rows - 2; + end = tty3270_tty_rows(tp); break; default: return; @@ -1564,7 +1570,7 @@ static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) line->len++; } - cy = min_t(int, tp->view.rows - 3, max_cy); + cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy); if (cy != tp->cy) { tty3270_convert_line(tp, tp->cy); tp->cy = cy; @@ -1947,7 +1953,7 @@ static void tty3270_hangup(struct tty_struct *tty) tty3270_reset_attributes(&tp->attributes); tty3270_reset_attributes(&tp->saved_attributes); tty3270_blank_screen(tp); - while (tp->nr_lines < tp->view.rows - 2) + while (tp->nr_lines < tty3270_tty_rows(tp)) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; spin_unlock_irq(&tp->view.lock); From b2057c870231edce4105c9825f8e5e1f20aebabc Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 24 Nov 2022 21:22:29 +0100 Subject: [PATCH 031/182] s390/tty3270: allocate screen with scrollback No functional change (except more memory consumption), in preparation for the line buffer rework. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 1c1390249342..d3c9fb27a03f 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -36,6 +36,8 @@ #define TTY3270_OUTPUT_BUFFER_SIZE 1024 #define TTY3270_STRING_PAGES 5 +#define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */ + static struct tty_driver *tty3270_driver; static int tty3270_max_index; static struct tty3270 *condev; @@ -56,6 +58,7 @@ struct tty3270_cell { struct tty3270_line { struct tty3270_cell *cells; int len; + int dirty; }; static const unsigned char sfq_read_partition[] = { @@ -92,6 +95,7 @@ struct tty3270 { unsigned int cx, cy; /* Current output position. */ struct tty3270_attribute attributes; struct tty3270_attribute saved_attributes; + int allocated_lines; struct tty3270_line *screen; /* Input stuff. */ @@ -805,22 +809,22 @@ static void tty3270_free_view(struct tty3270 *tp) /* * Allocate tty3270 screen. */ -static struct tty3270_line *tty3270_alloc_screen(unsigned int rows, unsigned int cols) +static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned int rows, + unsigned int cols, int *allocated_out) { struct tty3270_line *screen; - unsigned long size; - int lines; + int allocated, lines; - size = sizeof(struct tty3270_line) * (rows - 2); - screen = kzalloc(size, GFP_KERNEL); + allocated = __roundup_pow_of_two(rows) * TTY3270_SCREEN_PAGES; + screen = kcalloc(allocated, sizeof(struct tty3270_line), GFP_KERNEL); if (!screen) goto out_err; - for (lines = 0; lines < rows - 2; lines++) { - size = sizeof(struct tty3270_cell) * cols; - screen[lines].cells = kzalloc(size, GFP_KERNEL); + for (lines = 0; lines < allocated; lines++) { + screen[lines].cells = kcalloc(cols, sizeof(struct tty3270_cell), GFP_KERNEL); if (!screen[lines].cells) goto out_screen; } + *allocated_out = allocated; return screen; out_screen: while (lines--) @@ -833,11 +837,11 @@ static struct tty3270_line *tty3270_alloc_screen(unsigned int rows, unsigned int /* * Free tty3270 screen. */ -static void tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) +static void tty3270_free_screen(struct tty3270_line *screen, int old_lines) { int lines; - for (lines = 0; lines < rows - 2; lines++) + for (lines = 0; lines < old_lines; lines++) kfree(screen[lines].cells); kfree(screen); } @@ -853,6 +857,7 @@ static void tty3270_resize(struct raw3270_view *view, struct tty3270_line *screen, *oscreen; struct tty_struct *tty; struct winsize ws; + int new_allocated, old_allocated = tp->allocated_lines; if (old_model == new_model && old_cols == new_cols && @@ -863,7 +868,7 @@ static void tty3270_resize(struct raw3270_view *view, spin_unlock_irq(&tp->view.lock); return; } - screen = tty3270_alloc_screen(new_rows, new_cols); + screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated); if (IS_ERR(screen)) return; /* Switch to new output size */ @@ -871,6 +876,7 @@ static void tty3270_resize(struct raw3270_view *view, tty3270_blank_screen(tp); oscreen = tp->screen; tp->screen = screen; + tp->allocated_lines = new_allocated; tp->view.rows = new_rows; tp->view.cols = new_cols; tp->view.model = new_model; @@ -883,7 +889,7 @@ static void tty3270_resize(struct raw3270_view *view, tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; spin_unlock_irq(&tp->view.lock); - tty3270_free_screen(oscreen, old_rows); + tty3270_free_screen(oscreen, old_allocated); tty3270_set_timer(tp, 1); /* Informat tty layer about new size */ tty = tty_port_tty_get(&tp->port); @@ -920,7 +926,7 @@ static void tty3270_free(struct raw3270_view *view) struct tty3270 *tp = container_of(view, struct tty3270, view); del_timer_sync(&tp->timer); - tty3270_free_screen(tp->screen, tp->view.rows); + tty3270_free_screen(tp->screen, tp->allocated_lines); tty3270_free_view(tp); } @@ -969,7 +975,8 @@ tty3270_create_view(int index, struct tty3270 **newtp) return rc; } - tp->screen = tty3270_alloc_screen(tp->view.rows, tp->view.cols); + tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols, + &tp->allocated_lines); if (IS_ERR(tp->screen)) { rc = PTR_ERR(tp->screen); raw3270_put_view(&tp->view); From f77f936afe1ea9342725bff0bd0f7aaa54699794 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sat, 26 Nov 2022 20:18:21 +0100 Subject: [PATCH 032/182] s390/raw3270: make raw3270_buffer_address() accept x/y coordinates All callers of raw3270_buffer_address() are calculating the offset from some x/y coordinates. Move that calculation inside of the function, so user can pass the x/y values directly. Note that negative values are relative to the end-of-line or end-of-screen. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 21 ++++++++------------- drivers/s390/char/raw3270.c | 9 ++++++++- drivers/s390/char/raw3270.h | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index d3c9fb27a03f..0a4fce0b557d 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -133,6 +133,7 @@ struct tty3270 { #define TTY_UPDATE_ALL 16 /* Recreate screen. */ #define TTY3270_INPUT_AREA_ROWS 2 + static void tty3270_update(struct timer_list *); /* * Setup timeout for a device. On timeout trigger an update. @@ -153,7 +154,6 @@ static int tty3270_tty_rows(struct tty3270 *tp) static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) { struct string *line; - unsigned int off; line = tp->prompt; if (count != 0) @@ -168,8 +168,7 @@ static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) if (count < tp->view.cols * 2 - 11) { line->string[7 + count] = TO_RA; line->string[10 + count] = 0; - off = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string+count+8, off); + raw3270_buffer_address(tp->view.dev, line->string+count+8, -9, -1); line->len = 11 + count; } else line->len = 7 + count; @@ -183,7 +182,6 @@ static void tty3270_create_prompt(struct tty3270 *tp) /* empty input string */ TO_IC, TO_RA, 0, 0, 0 }; struct string *line; - unsigned int offset; line = alloc_string(&tp->freemem, sizeof(blueprint) + tp->view.cols * 2 - 9); @@ -193,10 +191,9 @@ static void tty3270_create_prompt(struct tty3270 *tp) memcpy(line->string, blueprint, sizeof(blueprint)); line->len = sizeof(blueprint); /* Set output offsets. */ - offset = tp->view.cols * tty3270_tty_rows(tp); - raw3270_buffer_address(tp->view.dev, line->string + 1, offset); - offset = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string + 8, offset); + + raw3270_buffer_address(tp->view.dev, line->string + 1, 0, -2); + raw3270_buffer_address(tp->view.dev, line->string + 8, -9, -1); /* Allocate input string for reading. */ tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6); @@ -232,7 +229,7 @@ static void tty3270_create_status(struct tty3270 *tp) memcpy(line->string, blueprint, sizeof(blueprint)); /* Set address to start of status string (= last 9 characters). */ offset = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string + 1, offset); + raw3270_buffer_address(tp->view.dev, line->string + 1, -9, -1); } /* @@ -243,12 +240,10 @@ static void tty3270_update_string(struct tty3270 *tp, struct string *line, int n { unsigned char *cp; - raw3270_buffer_address(tp->view.dev, line->string + 1, - tp->view.cols * nr); + raw3270_buffer_address(tp->view.dev, line->string + 1, 0, nr); cp = line->string + line->len - 4; if (*cp == TO_RA) - raw3270_buffer_address(tp->view.dev, cp + 1, - tp->view.cols * (nr + 1)); + raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1); } /* diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index e2d703e8ad48..6f55a094ad3e 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -113,8 +113,15 @@ static inline int raw3270_state_ready(struct raw3270 *rp) return rp->state == RAW3270_STATE_READY; } -void raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) +void raw3270_buffer_address(struct raw3270 *rp, char *cp, int x, int y) { + int addr; + + if (x < 0) + x = max_t(int, 0, rp->view->cols + x); + if (y < 0) + y = max_t(int, 0, rp->view->rows + y); + addr = (y * rp->view->cols) + x; if (test_bit(RAW3270_FLAGS_14BITADDR, &rp->flags)) { cp[0] = (addr >> 8) & 0x3f; cp[1] = addr & 0xff; diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 05cd501478ee..23efa6b8be49 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -132,7 +132,7 @@ raw3270_request_final(struct raw3270_request *rq) return list_empty(&rq->list); } -void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); +void raw3270_buffer_address(struct raw3270 *, char *, int, int); /* * Functions of a 3270 view. From 2b62ba58b362be224b0bfe4dd216374cd3ebcaac Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sat, 26 Nov 2022 23:24:43 +0100 Subject: [PATCH 033/182] s390/con3270: move tty3270_convert_line() To make the upcoming patches easier to read, move tty3270_convert_line() before changing code. No functional change. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 324 ++++++++++++++++++------------------ 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 0a4fce0b557d..3c4e9f8a50d0 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -362,6 +362,168 @@ static void tty3270_write_callback(struct raw3270_request *rq, void *data) xchg(&tp->write, rq); } +static int tty3270_required_length(struct tty3270 *tp, int line_nr) +{ + unsigned char f_color, b_color, highlight; + struct tty3270_line *line; + struct tty3270_cell *cell; + int i, flen = 3; /* Prefix (TO_SBA). */ + + line = tp->screen + line_nr; + flen += line->len; + highlight = TAX_RESET; + f_color = TAC_RESET; + b_color = TAC_RESET; + + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->attributes.highlight != highlight) { + flen += 3; /* TO_SA to switch highlight. */ + highlight = cell->attributes.highlight; + } + if (cell->attributes.f_color != f_color) { + flen += 3; /* TO_SA to switch color. */ + f_color = cell->attributes.f_color; + } + if (cell->attributes.b_color != b_color) { + flen += 3; /* TO_SA to switch color. */ + b_color = cell->attributes.b_color; + } + if (cell->attributes.alternate_charset) + flen += 1; /* TO_GE to switch to graphics extensions */ + } + if (highlight != TAX_RESET) + flen += 3; /* TO_SA to reset hightlight. */ + if (f_color != TAC_RESET) + flen += 3; /* TO_SA to reset color. */ + if (b_color != TAC_RESET) + flen += 3; /* TO_SA to reset color. */ + if (line->len < tp->view.cols) + flen += 4; /* Postfix (TO_RA). */ + + return flen; +} + +static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, + char *cp, struct tty3270_attribute *attr) +{ + if (attr->highlight != TAX_RESET) { + *cp++ = TO_SA; + *cp++ = TAT_EXTHI; + *cp++ = TAX_RESET; + } + if (attr->f_color != TAC_RESET) { + *cp++ = TO_SA; + *cp++ = TAT_FGCOLOR; + *cp++ = TAC_RESET; + } + if (attr->b_color != TAC_RESET) { + *cp++ = TO_SA; + *cp++ = TAT_BGCOLOR; + *cp++ = TAC_RESET; + } + if (line->len < tp->view.cols) { + *cp++ = TO_RA; + *cp++ = 0; + *cp++ = 0; + *cp++ = 0; + } + return cp; +} + +static char *tty3270_add_attributes(struct tty3270_line *line, struct tty3270_attribute *attr, + char *cp) +{ + struct tty3270_cell *cell; + int i; + + *cp++ = TO_SBA; + *cp++ = 0; + *cp++ = 0; + + for (i = 0, cell = line->cells; i < line->len; i++, cell++) { + if (cell->attributes.highlight != attr->highlight) { + *cp++ = TO_SA; + *cp++ = TAT_EXTHI; + *cp++ = cell->attributes.highlight; + attr->highlight = cell->attributes.highlight; + } + if (cell->attributes.f_color != attr->f_color) { + *cp++ = TO_SA; + *cp++ = TAT_FGCOLOR; + *cp++ = cell->attributes.f_color; + attr->f_color = cell->attributes.f_color; + } + if (cell->attributes.b_color != attr->b_color) { + *cp++ = TO_SA; + *cp++ = TAT_BGCOLOR; + *cp++ = cell->attributes.b_color; + attr->b_color = cell->attributes.b_color; + } + if (cell->attributes.alternate_charset) + *cp++ = TO_GE; + *cp++ = cell->character; + } + return cp; +} + +static void tty3270_reset_attributes(struct tty3270_attribute *attr) +{ + attr->highlight = TAX_RESET; + attr->f_color = TAC_RESET; + attr->b_color = TAC_RESET; +} + +static struct string *tty3270_resize_line(struct tty3270 *tp, struct string *s, int newlen) +{ + struct string *n = tty3270_alloc_string(tp, newlen); + + list_add(&n->list, &s->list); + list_del_init(&s->list); + if (!list_empty(&s->update)) + list_del_init(&s->update); + free_string(&tp->freemem, s); + return n; +} + +/* + * Convert a tty3270_line to a 3270 data fragment usable for output. + */ +static void tty3270_convert_line(struct tty3270 *tp, int line_nr) +{ + struct tty3270_line *line = tp->screen + line_nr; + struct tty3270_attribute attr; + struct string *s; + int flen, i; + char *cp; + + /* Determine how long the fragment will be. */ + flen = tty3270_required_length(tp, line_nr); + /* Find the line in the list. */ + i = tty3270_tty_rows(tp) - line_nr; + list_for_each_entry_reverse(s, &tp->lines, list) + if (--i <= 0) + break; + /* + * Check if the line needs to get reallocated. + */ + if (s->len != flen) + s = tty3270_resize_line(tp, s, flen); + + /* Write 3270 data fragment. */ + tty3270_reset_attributes(&attr); + cp = tty3270_add_attributes(line, &attr, s->string); + cp = tty3270_add_reset_attributes(tp, line, cp, &attr); + if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) { + /* Line is currently visible on screen. */ + tty3270_update_string(tp, s, line_nr); + /* Add line to update list. */ + if (list_empty(&s->update)) { + list_add_tail(&s->update, &tp->update); + tp->update_flags |= TTY_UPDATE_LIST; + } + } +} + /* * Update 3270 display. */ @@ -1109,18 +1271,6 @@ static char tty3270_graphics_translate(struct tty3270 *tp, char ch) } } -static struct string *tty3270_resize_line(struct tty3270 *tp, struct string *s, int newlen) -{ - struct string *n = tty3270_alloc_string(tp, newlen); - - list_add(&n->list, &s->list); - list_del_init(&s->list); - if (!list_empty(&s->update)) - list_del_init(&s->update); - free_string(&tp->freemem, s); - return n; -} - /* * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. @@ -1148,156 +1298,6 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) cell->attributes = tp->attributes; } -static int tty3270_required_length(struct tty3270 *tp, int line_nr) -{ - unsigned char f_color, b_color, highlight; - struct tty3270_line *line; - struct tty3270_cell *cell; - int i, flen = 3; /* Prefix (TO_SBA). */ - - line = tp->screen + line_nr; - flen += line->len; - highlight = TAX_RESET; - f_color = TAC_RESET; - b_color = TAC_RESET; - - for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->attributes.highlight != highlight) { - flen += 3; /* TO_SA to switch highlight. */ - highlight = cell->attributes.highlight; - } - if (cell->attributes.f_color != f_color) { - flen += 3; /* TO_SA to switch color. */ - f_color = cell->attributes.f_color; - } - if (cell->attributes.b_color != b_color) { - flen += 3; /* TO_SA to switch color. */ - b_color = cell->attributes.b_color; - } - if (cell->attributes.alternate_charset) - flen += 1; /* TO_GE to switch to graphics extensions */ - } - if (highlight != TAX_RESET) - flen += 3; /* TO_SA to reset hightlight. */ - if (f_color != TAC_RESET) - flen += 3; /* TO_SA to reset color. */ - if (b_color != TAC_RESET) - flen += 3; /* TO_SA to reset color. */ - if (line->len < tp->view.cols) - flen += 4; /* Postfix (TO_RA). */ - - return flen; -} - -static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, - char *cp, struct tty3270_attribute *attr) -{ - if (attr->highlight != TAX_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = TAX_RESET; - } - if (attr->f_color != TAC_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_FGCOLOR; - *cp++ = TAC_RESET; - } - if (attr->b_color != TAC_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_BGCOLOR; - *cp++ = TAC_RESET; - } - if (line->len < tp->view.cols) { - *cp++ = TO_RA; - *cp++ = 0; - *cp++ = 0; - *cp++ = 0; - } - return cp; -} - -static char *tty3270_add_attributes(struct tty3270_line *line, struct tty3270_attribute *attr, - char *cp) -{ - struct tty3270_cell *cell; - int i; - - *cp++ = TO_SBA; - *cp++ = 0; - *cp++ = 0; - - for (i = 0, cell = line->cells; i < line->len; i++, cell++) { - if (cell->attributes.highlight != attr->highlight) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = cell->attributes.highlight; - attr->highlight = cell->attributes.highlight; - } - if (cell->attributes.f_color != attr->f_color) { - *cp++ = TO_SA; - *cp++ = TAT_FGCOLOR; - *cp++ = cell->attributes.f_color; - attr->f_color = cell->attributes.f_color; - } - if (cell->attributes.b_color != attr->b_color) { - *cp++ = TO_SA; - *cp++ = TAT_BGCOLOR; - *cp++ = cell->attributes.b_color; - attr->b_color = cell->attributes.b_color; - } - if (cell->attributes.alternate_charset) - *cp++ = TO_GE; - *cp++ = cell->character; - } - return cp; -} - -static void tty3270_reset_attributes(struct tty3270_attribute *attr) -{ - attr->highlight = TAX_RESET; - attr->f_color = TAC_RESET; - attr->b_color = TAC_RESET; -} - -/* - * Convert a tty3270_line to a 3270 data fragment usable for output. - */ -static void tty3270_convert_line(struct tty3270 *tp, int line_nr) -{ - struct tty3270_line *line = tp->screen + line_nr; - struct tty3270_attribute attr; - struct string *s; - int flen, i; - char *cp; - - /* Determine how long the fragment will be. */ - flen = tty3270_required_length(tp, line_nr); - /* Find the line in the list. */ - i = tty3270_tty_rows(tp) - line_nr; - list_for_each_entry_reverse(s, &tp->lines, list) - if (--i <= 0) - break; - /* - * Check if the line needs to get reallocated. - */ - if (s->len != flen) - s = tty3270_resize_line(tp, s, flen); - - /* Write 3270 data fragment. */ - tty3270_reset_attributes(&attr); - cp = tty3270_add_attributes(line, &attr, s->string); - cp = tty3270_add_reset_attributes(tp, line, cp, &attr); - if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) { - /* Line is currently visible on screen. */ - tty3270_update_string(tp, s, line_nr); - /* Add line to update list. */ - if (list_empty(&s->update)) { - list_add_tail(&s->update, &tp->update); - tp->update_flags |= TTY_UPDATE_LIST; - } - } -} - /* * Do carriage return. */ From 6e49017ce41473daf1bef818c5dcc4a1e4a41252 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sat, 26 Nov 2022 23:39:16 +0100 Subject: [PATCH 034/182] s390/tty3270: move ASCII->EBCDIC conversion to convert_line() Instead of always converting the character set, only convert them when the line is really displayed. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 84 ++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 3c4e9f8a50d0..488d280a844d 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -430,8 +430,38 @@ static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_lin return cp; } -static char *tty3270_add_attributes(struct tty3270_line *line, struct tty3270_attribute *attr, - char *cp) +static char tty3270_graphics_translate(struct tty3270 *tp, char ch) +{ + switch (ch) { + case 'q': /* - */ + return 0xa2; + case 'x': /* '|' */ + return 0x85; + case 'l': /* |- */ + return 0xc5; + case 't': /* |_ */ + return 0xc6; + case 'u': /* _| */ + return 0xd6; + case 'k': /* -| */ + return 0xd5; + case 'j': + return 0xd4; + case 'm': + return 0xc4; + case 'n': /* + */ + return 0xd3; + case 'v': + return 0xc7; + case 'w': + return 0xd7; + default: + return ch; + } +} + +static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line, + struct tty3270_attribute *attr, char *cp) { struct tty3270_cell *cell; int i; @@ -459,9 +489,12 @@ static char *tty3270_add_attributes(struct tty3270_line *line, struct tty3270_at *cp++ = cell->attributes.b_color; attr->b_color = cell->attributes.b_color; } - if (cell->attributes.alternate_charset) + if (cell->attributes.alternate_charset) { *cp++ = TO_GE; - *cp++ = cell->character; + *cp++ = tty3270_graphics_translate(tp, cell->character); + } else { + *cp++ = tp->view.ascebc[(int)cell->character]; + } } return cp; } @@ -511,7 +544,7 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) /* Write 3270 data fragment. */ tty3270_reset_attributes(&attr); - cp = tty3270_add_attributes(line, &attr, s->string); + cp = tty3270_add_attributes(tp, line, &attr, s->string); cp = tty3270_add_reset_attributes(tp, line, cp, &attr); if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) { /* Line is currently visible on screen. */ @@ -1241,36 +1274,6 @@ static unsigned int tty3270_write_room(struct tty_struct *tty) return INT_MAX; } -static char tty3270_graphics_translate(struct tty3270 *tp, char ch) -{ - switch (ch) { - case 'q': /* - */ - return 0xa2; - case 'x': /* '|' */ - return 0x85; - case 'l': /* |- */ - return 0xc5; - case 't': /* |_ */ - return 0xc6; - case 'u': /* _| */ - return 0xd6; - case 'k': /* -| */ - return 0xd5; - case 'j': - return 0xd4; - case 'm': - return 0xc4; - case 'n': /* + */ - return 0xd3; - case 'v': - return 0xc7; - case 'w': - return 0xd7; - default: - return ch; - } -} - /* * Insert character into the screen at the current position with the * current color and highlight. This function does NOT do cursor movement. @@ -1284,17 +1287,14 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) if (line->len <= tp->cx) { while (line->len < tp->cx) { cell = line->cells + line->len; - cell->character = tp->view.ascebc[' ']; + cell->character = ' '; cell->attributes = tp->attributes; line->len++; } line->len++; } cell = line->cells + tp->cx; - if (tp->attributes.alternate_charset) - cell->character = tty3270_graphics_translate(tp, ch); - else - cell->character = tp->view.ascebc[(unsigned int)ch]; + cell->character = ch; cell->attributes = tp->attributes; } @@ -1339,7 +1339,7 @@ static void tty3270_ri(struct tty3270 *tp) static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) { - cell->character = tp->view.ascebc[' ']; + cell->character = ' '; tty3270_reset_attributes(&cell->attributes); } @@ -1363,7 +1363,7 @@ static void tty3270_insert_characters(struct tty3270 *tp, int n) if (line->len > tp->view.cols) line->len = tp->view.cols; while (n-- > 0) { - line->cells[tp->cx + n].character = tp->view.ascebc[' ']; + line->cells[tp->cx + n].character = ' '; line->cells[tp->cx + n].attributes = tp->attributes; } } From ae6572445b168ead43c0bfcb1fb2712968f43d1e Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 27 Nov 2022 20:43:53 +0100 Subject: [PATCH 035/182] s390/tty3270: add 3270 datastream helpers There are lots of places adding attributes or orders to the datastream. Add a few helpers to make that code shorter and easier to read. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 97 ++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 488d280a844d..4e37798ca5f8 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -148,6 +148,35 @@ static int tty3270_tty_rows(struct tty3270 *tp) return tp->view.rows - TTY3270_INPUT_AREA_ROWS; } +static char *tty3270_add_ba(struct tty3270 *tp, char *cp, char order, int x, int y) +{ + *cp++ = order; + raw3270_buffer_address(tp->view.dev, cp, x, y); + return cp + 2; +} + +static char *tty3270_add_ra(struct tty3270 *tp, char *cp, int x, int y, char c) +{ + cp = tty3270_add_ba(tp, cp, TO_RA, x, y); + *cp++ = c; + return cp; +} + +static char *tty3270_add_sa(struct tty3270 *tp, char *cp, char attr, char value) +{ + *cp++ = TO_SA; + *cp++ = attr; + *cp++ = value; + return cp; +} + +static char *tty3270_add_ge(struct tty3270 *tp, char *cp, char c) +{ + *cp++ = TO_GE; + *cp++ = c; + return cp; +} + /* * The input line are the two last lines of the screen. */ @@ -166,9 +195,7 @@ static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) line->string[6 + count] = TO_IC; /* Clear to end of input line. */ if (count < tp->view.cols * 2 - 11) { - line->string[7 + count] = TO_RA; - line->string[10 + count] = 0; - raw3270_buffer_address(tp->view.dev, line->string+count+8, -9, -1); + tty3270_add_ra(tp, line->string + count + 7, -9, -1, 0); line->len = 11 + count; } else line->len = 7 + count; @@ -213,7 +240,11 @@ static void tty3270_update_status(struct tty3270 *tp) tp->update_flags |= TTY_UPDATE_STATUS; } -static void tty3270_create_status(struct tty3270 *tp) +/* + * The status line is the last line of the screen. It shows the string + * "Running"/"Holding" in the lower right corner of the screen. + */ +static void tty3270_create_status(struct tty3270 * tp) { static const unsigned char blueprint[] = { TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR, TAC_GREEN, @@ -406,27 +437,14 @@ static int tty3270_required_length(struct tty3270 *tp, int line_nr) static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, char *cp, struct tty3270_attribute *attr) { - if (attr->highlight != TAX_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = TAX_RESET; - } - if (attr->f_color != TAC_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_FGCOLOR; - *cp++ = TAC_RESET; - } - if (attr->b_color != TAC_RESET) { - *cp++ = TO_SA; - *cp++ = TAT_BGCOLOR; - *cp++ = TAC_RESET; - } - if (line->len < tp->view.cols) { - *cp++ = TO_RA; - *cp++ = 0; - *cp++ = 0; - *cp++ = 0; - } + if (attr->highlight != TAX_RESET) + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + if (attr->f_color != TAC_RESET) + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET); + if (attr->b_color != TAC_RESET) + cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET); + if (line->len < tp->view.cols) + cp = tty3270_add_ra(tp, cp, 0, 0, 0); return cp; } @@ -464,37 +482,28 @@ static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *lin struct tty3270_attribute *attr, char *cp) { struct tty3270_cell *cell; - int i; + int c, i; - *cp++ = TO_SBA; - *cp++ = 0; - *cp++ = 0; + cp = tty3270_add_ba(tp, cp, TO_SBA, 0, 0); for (i = 0, cell = line->cells; i < line->len; i++, cell++) { if (cell->attributes.highlight != attr->highlight) { - *cp++ = TO_SA; - *cp++ = TAT_EXTHI; - *cp++ = cell->attributes.highlight; attr->highlight = cell->attributes.highlight; + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, attr->highlight); } if (cell->attributes.f_color != attr->f_color) { - *cp++ = TO_SA; - *cp++ = TAT_FGCOLOR; - *cp++ = cell->attributes.f_color; attr->f_color = cell->attributes.f_color; + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, attr->f_color); } if (cell->attributes.b_color != attr->b_color) { - *cp++ = TO_SA; - *cp++ = TAT_BGCOLOR; - *cp++ = cell->attributes.b_color; attr->b_color = cell->attributes.b_color; + cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, attr->b_color); } - if (cell->attributes.alternate_charset) { - *cp++ = TO_GE; - *cp++ = tty3270_graphics_translate(tp, cell->character); - } else { - *cp++ = tp->view.ascebc[(int)cell->character]; - } + c = cell->character; + if (cell->attributes.alternate_charset) + cp = tty3270_add_ge(tp, cp, tty3270_graphics_translate(tp, c)); + else + *cp++ = tp->view.ascebc[c]; } return cp; } From ec1b0a33a3828801233683dd4fbd07fe8a0e7909 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sat, 26 Nov 2022 19:34:56 +0100 Subject: [PATCH 036/182] s390/con3270: generate status line during output Updating the status line is almost the same as generating it when redrawing the screen. However, the code is much easier to read when doing so. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 84 ++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 4e37798ca5f8..e91a96a958e5 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -87,9 +87,9 @@ struct tty3270 { int nr_lines; /* # lines in list. */ int nr_up; /* # lines up in history. */ unsigned long update_flags; /* Update indication bits. */ - struct string *status; /* Lower right of display. */ struct raw3270_request *write; /* Single write request. */ struct timer_list timer; /* Output delay timer. */ + char *converted_line; /* RAW 3270 data stream */ /* Current tty screen. */ unsigned int cx, cy; /* Current output position. */ @@ -177,6 +177,13 @@ static char *tty3270_add_ge(struct tty3270 *tp, char *cp, char c) return cp; } +static char *tty3270_add_sf(struct tty3270 *tp, char *cp, char type) +{ + *cp++ = TO_SF; + *cp++ = type; + return cp; +} + /* * The input line are the two last lines of the screen. */ @@ -232,35 +239,28 @@ static void tty3270_create_prompt(struct tty3270 *tp) */ static void tty3270_update_status(struct tty3270 *tp) { - char *str; - - str = (tp->nr_up != 0) ? "History" : "Running"; - memcpy(tp->status->string + 8, str, 7); - codepage_convert(tp->view.ascebc, tp->status->string + 8, 7); tp->update_flags |= TTY_UPDATE_STATUS; + tty3270_set_timer(tp, 1); } /* * The status line is the last line of the screen. It shows the string * "Running"/"Holding" in the lower right corner of the screen. */ -static void tty3270_create_status(struct tty3270 * tp) +static int tty3270_add_status(struct tty3270 *tp) { - static const unsigned char blueprint[] = { - TO_SBA, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR, TAC_GREEN, - 0, 0, 0, 0, 0, 0, 0, TO_SF, TF_LOG, TO_SA, TAT_FGCOLOR, - TAC_RESET - }; - struct string *line; - unsigned int offset; + char *cp = tp->converted_line; + int len; - line = alloc_string(&tp->freemem,sizeof(blueprint)); - tp->status = line; - /* Copy blueprint to status line */ - memcpy(line->string, blueprint, sizeof(blueprint)); - /* Set address to start of status string (= last 9 characters). */ - offset = tp->view.cols * tp->view.rows - 9; - raw3270_buffer_address(tp->view.dev, line->string + 1, -9, -1); + cp = tty3270_add_ba(tp, cp, TO_SBA, -9, -1); + cp = tty3270_add_sf(tp, cp, TF_LOG); + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_GREEN); + len = sprintf(cp, tp->nr_up ? "History" : "Running"); + codepage_convert(tp->view.ascebc, cp, len); + cp += len; + cp = tty3270_add_sf(tp, cp, TF_LOG); + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET); + return cp - (char *)tp->converted_line; } /* @@ -589,7 +589,6 @@ static void tty3270_update(struct timer_list *t) updated = 0; if (tp->update_flags & TTY_UPDATE_ALL) { tty3270_rebuild_update(tp); - tty3270_update_status(tp); tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST | TTY_UPDATE_INPUT | TTY_UPDATE_STATUS; } @@ -606,10 +605,11 @@ static void tty3270_update(struct timer_list *t) /* * Update status line. */ - if (tp->update_flags & TTY_UPDATE_STATUS) - if (raw3270_request_add_data(wrq, tp->status->string, - tp->status->len) == 0) + if (tp->update_flags & TTY_UPDATE_STATUS) { + len = tty3270_add_status(tp); + if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) updated |= TTY_UPDATE_STATUS; + } /* * Write input line. @@ -729,7 +729,7 @@ static void tty3270_scroll_forward(struct kbd_data *kbd) if (nr_up != tp->nr_up) { tp->nr_up = nr_up; tty3270_rebuild_update(tp); - tty3270_update_status(tp); + tp->update_flags |= TTY_UPDATE_STATUS; tty3270_set_timer(tp, 1); } spin_unlock_irq(&tp->view.lock); @@ -751,7 +751,6 @@ static void tty3270_scroll_backward(struct kbd_data *kbd) tp->nr_up = nr_up; tty3270_rebuild_update(tp); tty3270_update_status(tp); - tty3270_set_timer(tp, 1); } spin_unlock_irq(&tp->view.lock); } @@ -1081,9 +1080,7 @@ static void tty3270_resize(struct raw3270_view *view, tp->view.model = new_model; free_string(&tp->freemem, tp->prompt); - free_string(&tp->freemem, tp->status); tty3270_create_prompt(tp); - tty3270_create_status(tp); while (tp->nr_lines < tty3270_tty_rows(tp)) tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; @@ -1126,6 +1123,7 @@ static void tty3270_free(struct raw3270_view *view) del_timer_sync(&tp->timer); tty3270_free_screen(tp->screen, tp->allocated_lines); + free_page((unsigned long)tp->converted_line); tty3270_free_view(tp); } @@ -1169,24 +1167,23 @@ tty3270_create_view(int index, struct tty3270 **newtp) rc = raw3270_add_view(&tp->view, &tty3270_fn, index + RAW3270_FIRSTMINOR, RAW3270_VIEW_LOCK_IRQ); - if (rc) { - tty3270_free_view(tp); - return rc; - } + if (rc) + goto out_free_view; tp->screen = tty3270_alloc_screen(tp, tp->view.rows, tp->view.cols, &tp->allocated_lines); if (IS_ERR(tp->screen)) { rc = PTR_ERR(tp->screen); - raw3270_put_view(&tp->view); - raw3270_del_view(&tp->view); - tty3270_free_view(tp); - return rc; + goto out_put_view; + } + + tp->converted_line = (void *)__get_free_page(GFP_KERNEL); + if (!tp->converted_line) { + rc = -ENOMEM; + goto out_free_screen; } tty3270_create_prompt(tp); - tty3270_create_status(tp); - tty3270_update_status(tp); /* Create blank line for every line in the tty output area. */ for (i = 0; i < tty3270_tty_rows(tp); i++) @@ -1203,6 +1200,15 @@ tty3270_create_view(int index, struct tty3270 **newtp) raw3270_put_view(&tp->view); *newtp = tp; return 0; + +out_free_screen: + tty3270_free_screen(tp->screen, tp->view.rows); +out_put_view: + raw3270_put_view(&tp->view); + raw3270_del_view(&tp->view); +out_free_view: + tty3270_free_view(tp); + return rc; } /* From 9c138af9b777f466226787cf24a34ff94d4f80e2 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 24 Nov 2022 22:53:44 +0100 Subject: [PATCH 037/182] s390/tty3270: convert lines during output The length of the screen line is variable with the 3270 protocol. For each attribute (foreground, background color, highlighting etc) we need 3 bytes: the set attribute order, the attribute number, and the value of the attribute. This means that depending on screen content, we might end up 3*3 bytes addtional data for a single character. Allocating the maximum possible amount of memory is quite a lot, and we cannot easily extend the lines by allocating memory because we might get called from atomic context. Failing to extend the memory would also be bad as that would mean that we could miss kernel messages in oom conditions. Therefore move the conversion to a 3270 datastream to tty3270_update(), and use only single line buffer. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 338 +++++++++++------------------------- drivers/s390/char/raw3270.h | 2 - 2 files changed, 102 insertions(+), 238 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index e91a96a958e5..00e6e01cf8c8 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -33,7 +33,7 @@ #include "keyboard.h" #define TTY3270_CHAR_BUF_SIZE 256 -#define TTY3270_OUTPUT_BUFFER_SIZE 1024 +#define TTY3270_OUTPUT_BUFFER_SIZE 4096 #define TTY3270_STRING_PAGES 5 #define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */ @@ -82,7 +82,6 @@ struct tty3270 { /* Output stuff. */ struct list_head lines; /* List of lines. */ - struct list_head update; /* List of lines to update. */ unsigned char wcc; /* Write control character. */ int nr_lines; /* # lines in list. */ int nr_up; /* # lines up in history. */ @@ -90,6 +89,8 @@ struct tty3270 { struct raw3270_request *write; /* Single write request. */ struct timer_list timer; /* Output delay timer. */ char *converted_line; /* RAW 3270 data stream */ + unsigned int line_view_start; /* Start of visible area */ + unsigned int line_write_start; /* current write position */ /* Current tty screen. */ unsigned int cx, cy; /* Current output position. */ @@ -127,14 +128,12 @@ struct tty3270 { /* tty3270->update_flags. See tty3270_update for details. */ #define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ -#define TTY_UPDATE_LIST 2 /* Update lines in tty3270->update. */ #define TTY_UPDATE_INPUT 4 /* Update input line. */ #define TTY_UPDATE_STATUS 8 /* Update status line. */ #define TTY_UPDATE_ALL 16 /* Recreate screen. */ #define TTY3270_INPUT_AREA_ROWS 2 -static void tty3270_update(struct timer_list *); /* * Setup timeout for a device. On timeout trigger an update. */ @@ -184,6 +183,21 @@ static char *tty3270_add_sf(struct tty3270 *tp, char *cp, char type) return cp; } +static int tty3270_line_increment(struct tty3270 *tp, unsigned int line, unsigned int incr) +{ + return (line + incr) & (tp->allocated_lines - 1); +} + +static struct tty3270_line *tty3270_get_write_line(struct tty3270 *tp, unsigned int num) +{ + return tp->screen + tty3270_line_increment(tp, tp->line_write_start, num); +} + +static struct tty3270_line *tty3270_get_view_line(struct tty3270 *tp, unsigned int num) +{ + return tp->screen + tty3270_line_increment(tp, tp->line_view_start, num - tp->nr_up); +} + /* * The input line are the two last lines of the screen. */ @@ -233,16 +247,6 @@ static void tty3270_create_prompt(struct tty3270 *tp) tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6); } -/* - * The status line is the last line of the screen. It shows the string - * "Running"/"Holding" in the lower right corner of the screen. - */ -static void tty3270_update_status(struct tty3270 *tp) -{ - tp->update_flags |= TTY_UPDATE_STATUS; - tty3270_set_timer(tp, 1); -} - /* * The status line is the last line of the screen. It shows the string * "Running"/"Holding" in the lower right corner of the screen. @@ -267,45 +271,15 @@ static int tty3270_add_status(struct tty3270 *tp) * Set output offsets to 3270 datastream fragment of a tty string. * (TO_SBA offset at the start and TO_RA offset at the end of the string) */ -static void tty3270_update_string(struct tty3270 *tp, struct string *line, int nr) +static void tty3270_update_string(struct tty3270 *tp, char *line, int len, int nr) { unsigned char *cp; - - raw3270_buffer_address(tp->view.dev, line->string + 1, 0, nr); - cp = line->string + line->len - 4; + raw3270_buffer_address(tp->view.dev, line + 1, 0, nr); + cp = line + len - 4; if (*cp == TO_RA) raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1); } -/* - * Rebuild update list to print all lines. - */ -static void tty3270_rebuild_update(struct tty3270 *tp) -{ - struct string *s, *n; - int line, nr_up; - - /* - * Throw away update list and create a new one, - * containing all lines that will fit on the screen. - */ - list_for_each_entry_safe(s, n, &tp->update, update) - list_del_init(&s->update); - line = tty3270_tty_rows(tp) - 1; - nr_up = tp->nr_up; - list_for_each_entry_reverse(s, &tp->lines, list) { - if (nr_up > 0) { - nr_up--; - continue; - } - tty3270_update_string(tp, s, line); - list_add(&s->update, &tp->update); - if (--line < 0) - break; - } - tp->update_flags |= TTY_UPDATE_LIST; -} - /* * Alloc string for size bytes. If there is not enough room in * freemem, free strings until there is room. @@ -318,63 +292,24 @@ static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size) if (s) return s; list_for_each_entry_safe(s, n, &tp->lines, list) { - BUG_ON(tp->nr_lines <= tty3270_tty_rows(tp)); list_del(&s->list); - if (!list_empty(&s->update)) - list_del(&s->update); - tp->nr_lines--; if (free_string(&tp->freemem, s) >= size) break; } - s = alloc_string(&tp->freemem, size); - BUG_ON(!s); - if (tp->nr_up != 0 && - tp->nr_up + tty3270_tty_rows(tp) >= tp->nr_lines) { - tp->nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - } - return s; + return alloc_string(&tp->freemem, size); } -/* - * Add an empty line to the list. - */ -static void tty3270_blank_line(struct tty3270 *tp) -{ - static const unsigned char blueprint[] = { - TO_SBA, 0, 0, TO_SA, TAT_EXTHI, TAX_RESET, - TO_SA, TAT_FGCOLOR, TAC_RESET, TO_RA, 0, 0, 0, - }; - struct string *s; - - s = tty3270_alloc_string(tp, sizeof(blueprint)); - memcpy(s->string, blueprint, sizeof(blueprint)); - s->len = sizeof(blueprint); - list_add_tail(&s->list, &tp->lines); - tp->nr_lines++; - if (tp->nr_up != 0) - tp->nr_up++; -} - -/* - * Create a blank screen and remove all lines from the history. - */ static void tty3270_blank_screen(struct tty3270 *tp) { - struct string *s, *n; + struct tty3270_line *line; int i; - for (i = 0; i < tty3270_tty_rows(tp); i++) - tp->screen[i].len = 0; - tp->nr_up = 0; - list_for_each_entry_safe(s, n, &tp->lines, list) { - list_del(&s->list); - if (!list_empty(&s->update)) - list_del(&s->update); - tp->nr_lines--; - free_string(&tp->freemem, s); + for (i = 0; i < tty3270_tty_rows(tp); i++) { + line = tty3270_get_write_line(tp, i); + line->len = 0; + line->dirty = 1; } + tp->nr_up = 0; } /* @@ -393,14 +328,12 @@ static void tty3270_write_callback(struct raw3270_request *rq, void *data) xchg(&tp->write, rq); } -static int tty3270_required_length(struct tty3270 *tp, int line_nr) +static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line) { unsigned char f_color, b_color, highlight; - struct tty3270_line *line; struct tty3270_cell *cell; int i, flen = 3; /* Prefix (TO_SBA). */ - line = tp->screen + line_nr; flen += line->len; highlight = TAX_RESET; f_color = TAC_RESET; @@ -515,55 +448,24 @@ static void tty3270_reset_attributes(struct tty3270_attribute *attr) attr->b_color = TAC_RESET; } -static struct string *tty3270_resize_line(struct tty3270 *tp, struct string *s, int newlen) -{ - struct string *n = tty3270_alloc_string(tp, newlen); - - list_add(&n->list, &s->list); - list_del_init(&s->list); - if (!list_empty(&s->update)) - list_del_init(&s->update); - free_string(&tp->freemem, s); - return n; -} - /* * Convert a tty3270_line to a 3270 data fragment usable for output. */ -static void tty3270_convert_line(struct tty3270 *tp, int line_nr) +static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line) { - struct tty3270_line *line = tp->screen + line_nr; struct tty3270_attribute attr; - struct string *s; - int flen, i; + int flen; char *cp; /* Determine how long the fragment will be. */ - flen = tty3270_required_length(tp, line_nr); - /* Find the line in the list. */ - i = tty3270_tty_rows(tp) - line_nr; - list_for_each_entry_reverse(s, &tp->lines, list) - if (--i <= 0) - break; - /* - * Check if the line needs to get reallocated. - */ - if (s->len != flen) - s = tty3270_resize_line(tp, s, flen); - + flen = tty3270_required_length(tp, line); + if (flen > PAGE_SIZE) + return 0; /* Write 3270 data fragment. */ tty3270_reset_attributes(&attr); - cp = tty3270_add_attributes(tp, line, &attr, s->string); + cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line); cp = tty3270_add_reset_attributes(tp, line, cp, &attr); - if (tp->nr_up + line_nr < tty3270_tty_rows(tp)) { - /* Line is currently visible on screen. */ - tty3270_update_string(tp, s, line_nr); - /* Add line to update list. */ - if (list_empty(&s->update)) { - list_add_tail(&s->update, &tp->update); - tp->update_flags |= TTY_UPDATE_LIST; - } - } + return cp - (char *)tp->converted_line; } /* @@ -572,12 +474,10 @@ static void tty3270_convert_line(struct tty3270 *tp, int line_nr) static void tty3270_update(struct timer_list *t) { struct tty3270 *tp = from_timer(tp, t, timer); - static char invalid_sba[2] = { 0xff, 0xff }; struct raw3270_request *wrq; + struct tty3270_line *line; unsigned long updated; - struct string *s, *n; - char *sba, *str; - int rc, len; + int i, rc, len; wrq = xchg(&tp->write, 0); if (!wrq) { @@ -588,8 +488,7 @@ static void tty3270_update(struct timer_list *t) spin_lock_irq(&tp->view.lock); updated = 0; if (tp->update_flags & TTY_UPDATE_ALL) { - tty3270_rebuild_update(tp); - tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_LIST | + tp->update_flags = TTY_UPDATE_ERASE | TTY_UPDATE_INPUT | TTY_UPDATE_STATUS; } if (tp->update_flags & TTY_UPDATE_ERASE) { @@ -619,33 +518,20 @@ static void tty3270_update(struct timer_list *t) tp->prompt->len) == 0) updated |= TTY_UPDATE_INPUT; - sba = invalid_sba; - - if (tp->update_flags & TTY_UPDATE_LIST) { - /* Write strings in the update list to the screen. */ - list_for_each_entry_safe(s, n, &tp->update, update) { - str = s->string; - len = s->len; - /* - * Skip TO_SBA at the start of the string if the - * last output position matches the start address - * of this line. - */ - if (s->string[1] == sba[0] && s->string[2] == sba[1]) { - str += 3; - len -= 3; - } - if (raw3270_request_add_data(wrq, str, len) != 0) - break; - list_del_init(&s->update); - if (s->string[s->len - 4] == TO_RA) - sba = s->string + s->len - 3; - else - sba = invalid_sba; - } - if (list_empty(&tp->update)) - updated |= TTY_UPDATE_LIST; + for (i = 0; i < tty3270_tty_rows(tp); i++) { + line = tty3270_get_view_line(tp, i); + if (!line->dirty) + continue; + len = tty3270_convert_line(tp, line); + tty3270_update_string(tp, tp->converted_line, len, i); + if (raw3270_request_add_data(wrq, tp->converted_line, len)) + break; + line->dirty = 0; } + + if (i < tty3270_tty_rows(tp) - 1) + tty3270_set_timer(tp, 1); + wrq->callback = tty3270_write_callback; rc = raw3270_start(&tp->view, wrq); if (rc == 0) { @@ -676,6 +562,8 @@ static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len) tp->rcl_nr--; } s = tty3270_alloc_string(tp, len); + if (!s) + return; memcpy(s->string, input, len); list_add_tail(&s->list, &tp->rcl_lines); tp->rcl_nr++; @@ -714,24 +602,29 @@ static void tty3270_exit_tty(struct kbd_data *kbd) raw3270_deactivate_view(&tp->view); } +static void tty3270_redraw(struct tty3270 *tp) +{ + int i; + + for (i = 0; i < tty3270_tty_rows(tp); i++) + tty3270_get_view_line(tp, i)->dirty = 1; + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); +} /* * Scroll forward in history. */ static void tty3270_scroll_forward(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - int nr_up; spin_lock_irq(&tp->view.lock); - nr_up = tp->nr_up - tp->view.rows + TTY3270_INPUT_AREA_ROWS; - if (nr_up < 0) - nr_up = 0; - if (nr_up != tp->nr_up) { - tp->nr_up = nr_up; - tty3270_rebuild_update(tp); - tp->update_flags |= TTY_UPDATE_STATUS; - tty3270_set_timer(tp, 1); - } + + if (tp->nr_up >= tty3270_tty_rows(tp)) + tp->nr_up -= tty3270_tty_rows(tp) / 2; + else + tp->nr_up = 0; + tty3270_redraw(tp); spin_unlock_irq(&tp->view.lock); } @@ -741,17 +634,12 @@ static void tty3270_scroll_forward(struct kbd_data *kbd) static void tty3270_scroll_backward(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - int nr_up; spin_lock_irq(&tp->view.lock); - nr_up = tp->nr_up + tty3270_tty_rows(tp); - if (nr_up + tty3270_tty_rows(tp) > tp->nr_lines) - nr_up = tp->nr_lines - tp->view.rows + TTY3270_INPUT_AREA_ROWS; - if (nr_up != tp->nr_up) { - tp->nr_up = nr_up; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - } + tp->nr_up += tty3270_tty_rows(tp) / 2; + if (tp->nr_up > tp->allocated_lines - tty3270_tty_rows(tp)) + tp->nr_up = tp->allocated_lines - tty3270_tty_rows(tp); + tty3270_redraw(tp); spin_unlock_irq(&tp->view.lock); } @@ -781,11 +669,8 @@ static void tty3270_read_tasklet(unsigned long data) len = tp->input->len - 6 - rrq->rescnt; if (tp->inattr != TF_INPUTN) tty3270_rcl_add(tp, input, len); - if (tp->nr_up > 0) { + if (tp->nr_up > 0) tp->nr_up = 0; - tty3270_rebuild_update(tp); - tty3270_update_status(tp); - } /* Clear input area. */ tty3270_update_prompt(tp, NULL, 0); tty3270_set_timer(tp, 1); @@ -931,7 +816,6 @@ static struct tty3270 *tty3270_alloc_view(void) goto out_tp; INIT_LIST_HEAD(&tp->freemem); INIT_LIST_HEAD(&tp->lines); - INIT_LIST_HEAD(&tp->update); INIT_LIST_HEAD(&tp->rcl_lines); tp->rcl_max = 20; @@ -1000,6 +884,7 @@ static void tty3270_free_view(struct tty3270 *tp) for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) free_pages((unsigned long) tp->freemem_pages[pages], 0); kfree(tp->freemem_pages); + free_page((unsigned long)tp->converted_line); tty_port_destroy(&tp->port); kfree(tp); } @@ -1061,8 +946,7 @@ static void tty3270_resize(struct raw3270_view *view, old_cols == new_cols && old_rows == new_rows) { spin_lock_irq(&tp->view.lock); - tp->update_flags = TTY_UPDATE_ALL; - tty3270_set_timer(tp, 1); + tty3270_redraw(tp); spin_unlock_irq(&tp->view.lock); return; } @@ -1081,8 +965,6 @@ static void tty3270_resize(struct raw3270_view *view, free_string(&tp->freemem, tp->prompt); tty3270_create_prompt(tp); - while (tp->nr_lines < tty3270_tty_rows(tp)) - tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; spin_unlock_irq(&tp->view.lock); tty3270_free_screen(oscreen, old_allocated); @@ -1154,7 +1036,7 @@ static int tty3270_create_view(int index, struct tty3270 **newtp) { struct tty3270 *tp; - int i, rc; + int rc; if (tty3270_max_index < index + 1) tty3270_max_index = index + 1; @@ -1186,8 +1068,7 @@ tty3270_create_view(int index, struct tty3270 **newtp) tty3270_create_prompt(tp); /* Create blank line for every line in the tty output area. */ - for (i = 0; i < tty3270_tty_rows(tp); i++) - tty3270_blank_line(tp); + tty3270_blank_screen(tp); tp->kbd->port = &tp->port; tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty; @@ -1298,7 +1179,7 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) struct tty3270_line *line; struct tty3270_cell *cell; - line = tp->screen + tp->cy; + line = tty3270_get_write_line(tp, tp->cy); if (line->len <= tp->cx) { while (line->len < tp->cx) { cell = line->cells + line->len; @@ -1311,6 +1192,7 @@ static void tty3270_put_character(struct tty3270 *tp, char ch) cell = line->cells + tp->cx; cell->character = ch; cell->attributes = tp->attributes; + line->dirty = 1; } /* @@ -1326,30 +1208,27 @@ static void tty3270_cr(struct tty3270 *tp) */ static void tty3270_lf(struct tty3270 *tp) { - struct tty3270_line temp; + struct tty3270_line *line; int i; - tty3270_convert_line(tp, tp->cy); if (tp->cy < tty3270_tty_rows(tp) - 1) { tp->cy++; - return; + } else { + tp->line_view_start = tty3270_line_increment(tp, tp->line_view_start, 1); + tp->line_write_start = tty3270_line_increment(tp, tp->line_write_start, 1); + for (i = 0; i < tty3270_tty_rows(tp); i++) + tty3270_get_view_line(tp, i)->dirty = 1; } - /* Last line just filled up. Add new, blank line. */ - tty3270_blank_line(tp); - temp = tp->screen[0]; - temp.len = 0; - for (i = 0; i < tty3270_tty_rows(tp) - 1; i++) - tp->screen[i] = tp->screen[i+1]; - tp->screen[tty3270_tty_rows(tp) - 1] = temp; - tty3270_rebuild_update(tp); + + line = tty3270_get_write_line(tp, tp->cy); + line->len = 0; + line->dirty = 1; } static void tty3270_ri(struct tty3270 *tp) { - if (tp->cy > 0) { - tty3270_convert_line(tp, tp->cy); - tp->cy--; - } + if (tp->cy > 0) + tp->cy--; } static void tty3270_reset_cell(struct tty3270 *tp, struct tty3270_cell *cell) @@ -1366,7 +1245,7 @@ static void tty3270_insert_characters(struct tty3270 *tp, int n) struct tty3270_line *line; int k; - line = tp->screen + tp->cy; + line = tty3270_get_write_line(tp, tp->cy); while (line->len < tp->cx) tty3270_reset_cell(tp, &line->cells[line->len++]); if (n > tp->view.cols - tp->cx) @@ -1391,7 +1270,7 @@ static void tty3270_delete_characters(struct tty3270 *tp, int n) struct tty3270_line *line; int i; - line = tp->screen + tp->cy; + line = tty3270_get_write_line(tp, tp->cy); if (line->len <= tp->cx) return; if (line->len - tp->cx <= n) { @@ -1411,7 +1290,7 @@ static void tty3270_erase_characters(struct tty3270 *tp, int n) struct tty3270_line *line; struct tty3270_cell *cell; - line = tp->screen + tp->cy; + line = tty3270_get_write_line(tp, tp->cy); while (line->len > tp->cx && n-- > 0) { cell = line->cells + tp->cx++; tty3270_reset_cell(tp, cell); @@ -1432,7 +1311,7 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) struct tty3270_cell *cell; int i, start, end; - line = tp->screen + tp->cy; + line = tty3270_get_write_line(tp, tp->cy); switch (mode) { case 0: @@ -1459,8 +1338,6 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) if (line->len <= end) line->len = end; - - tty3270_convert_line(tp, tp->cy); } /* @@ -1471,6 +1348,7 @@ static void tty3270_erase_line(struct tty3270 *tp, int mode) */ static void tty3270_erase_display(struct tty3270 *tp, int mode) { + struct tty3270_line *line; int i, start, end; switch (mode) { @@ -1492,10 +1370,10 @@ static void tty3270_erase_display(struct tty3270 *tp, int mode) return; } for (i = start; i < end; i++) { - tp->screen[i].len = 0; - tty3270_convert_line(tp, i); + line = tty3270_get_write_line(tp, i); + line->len = 0; + line->dirty = 1; } - tty3270_rebuild_update(tp); } /* @@ -1579,19 +1457,14 @@ static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) int max_cy = max(0, cy); tp->cx = min_t(int, tp->view.cols - 1, max_cx); - line = tp->screen + tp->cy; + line = tty3270_get_write_line(tp, tp->cy); while (line->len < tp->cx) { cell = line->cells + line->len; cell->character = ' '; cell->attributes = tp->attributes; line->len++; } - - cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy); - if (cy != tp->cy) { - tty3270_convert_line(tp, tp->cy); - tp->cy = cy; - } + tp->cy = min_t(int, tty3270_tty_rows(tp) - 1, max_cy); } /* @@ -1652,7 +1525,6 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) tp->saved_attributes = tp->attributes; break; case '8': /* Restore cursor position. */ - tty3270_convert_line(tp, tp->cy); tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); tp->attributes = tp->saved_attributes; break; @@ -1767,7 +1639,6 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) tp->saved_attributes = tp->attributes; break; case 'u': /* Restore cursor position. */ - tty3270_convert_line(tp, tp->cy); tty3270_goto_xy(tp, tp->saved_cx, tp->saved_cy); tp->attributes = tp->saved_attributes; break; @@ -1843,9 +1714,6 @@ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, break; } } - /* Convert current line to 3270 data fragment. */ - tty3270_convert_line(tp, tp->cy); - /* Setup timer to update display after 1/10 second */ if (!timer_pending(&tp->timer)) tty3270_set_timer(tp, HZ/10); @@ -1970,8 +1838,6 @@ static void tty3270_hangup(struct tty_struct *tty) tty3270_reset_attributes(&tp->attributes); tty3270_reset_attributes(&tp->saved_attributes); tty3270_blank_screen(tp); - while (tp->nr_lines < tty3270_tty_rows(tp)) - tty3270_blank_line(tp); tp->update_flags = TTY_UPDATE_ALL; spin_unlock_irq(&tp->view.lock); tty3270_set_timer(tp, 1); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 23efa6b8be49..5ca1af6df935 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -219,7 +219,6 @@ void raw3270_unregister_notifier(struct raw3270_notifier *); struct string { struct list_head list; - struct list_head update; unsigned long size; unsigned long len; char string[]; @@ -245,7 +244,6 @@ alloc_string(struct list_head *free_list, unsigned long len) list_del(&cs->list); cs->len = len; INIT_LIST_HEAD(&cs->list); - INIT_LIST_HEAD(&cs->update); return cs; } return NULL; From 164eb669348045894b50eaecc5936ec07b4307f0 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 07:31:49 +0100 Subject: [PATCH 038/182] s390/tty3270: use normal char buffer for prompt/input Preparation patch to allow removing the custom 3270 memory allocator. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 140 +++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 57 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 00e6e01cf8c8..e3eee0492fea 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -100,8 +100,8 @@ struct tty3270 { struct tty3270_line *screen; /* Input stuff. */ - struct string *prompt; /* Output string for input area. */ - struct string *input; /* Input string for read request. */ + char *prompt; /* Output string for input area. */ + char *input; /* Input string for read request. */ struct raw3270_request *read; /* Single read request. */ struct raw3270_request *kreset; /* Single keyboard reset request. */ struct raw3270_request *readpartreq; @@ -198,53 +198,44 @@ static struct tty3270_line *tty3270_get_view_line(struct tty3270 *tp, unsigned i return tp->screen + tty3270_line_increment(tp, tp->line_view_start, num - tp->nr_up); } +static int tty3270_input_size(int cols) +{ + return cols * 2 - 11; +} + +static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) +{ + memcpy(tp->prompt, input, count); + tp->prompt[count] = '\0'; + tp->update_flags |= TTY_UPDATE_INPUT; + tty3270_set_timer(tp, 1); +} + /* * The input line are the two last lines of the screen. */ -static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) +static int tty3270_add_prompt(struct tty3270 *tp) { - struct string *line; + int count = 0; + char *cp; - line = tp->prompt; - if (count != 0) - line->string[5] = TF_INMDT; - else - line->string[5] = tp->inattr; - if (count > tp->view.cols * 2 - 11) - count = tp->view.cols * 2 - 11; - memcpy(line->string + 6, input, count); - line->string[6 + count] = TO_IC; + cp = tp->converted_line; + cp = tty3270_add_ba(tp, cp, TO_SBA, 0, -2); + *cp++ = tp->view.ascebc['>']; + + if (*tp->prompt) { + cp = tty3270_add_sf(tp, cp, TF_INMDT); + count = min_t(int, strlen(tp->prompt), tp->view.cols * 2 - 11); + memcpy(cp, tp->prompt, count); + cp += count; + } else { + cp = tty3270_add_sf(tp, cp, tp->inattr); + } + *cp++ = TO_IC; /* Clear to end of input line. */ - if (count < tp->view.cols * 2 - 11) { - tty3270_add_ra(tp, line->string + count + 7, -9, -1, 0); - line->len = 11 + count; - } else - line->len = 7 + count; - tp->update_flags |= TTY_UPDATE_INPUT; -} - -static void tty3270_create_prompt(struct tty3270 *tp) -{ - static const unsigned char blueprint[] = - { TO_SBA, 0, 0, 0x6e, TO_SF, TF_INPUT, - /* empty input string */ - TO_IC, TO_RA, 0, 0, 0 }; - struct string *line; - - line = alloc_string(&tp->freemem, - sizeof(blueprint) + tp->view.cols * 2 - 9); - tp->prompt = line; - tp->inattr = TF_INPUT; - /* Copy blueprint to status line */ - memcpy(line->string, blueprint, sizeof(blueprint)); - line->len = sizeof(blueprint); - /* Set output offsets. */ - - raw3270_buffer_address(tp->view.dev, line->string + 1, 0, -2); - raw3270_buffer_address(tp->view.dev, line->string + 8, -9, -1); - - /* Allocate input string for reading. */ - tp->input = alloc_string(&tp->freemem, tp->view.cols * 2 - 9 + 6); + if (count < tp->view.cols * 2 - 11) + cp = tty3270_add_ra(tp, cp, -9, -1, 0); + return cp - tp->converted_line; } /* @@ -513,10 +504,11 @@ static void tty3270_update(struct timer_list *t) /* * Write input line. */ - if (tp->update_flags & TTY_UPDATE_INPUT) - if (raw3270_request_add_data(wrq, tp->prompt->string, - tp->prompt->len) == 0) + if (tp->update_flags & TTY_UPDATE_INPUT) { + len = tty3270_add_prompt(tp); + if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) updated |= TTY_UPDATE_INPUT; + } for (i = 0; i < tty3270_tty_rows(tp); i++) { line = tty3270_get_view_line(tp, i); @@ -662,11 +654,11 @@ static void tty3270_read_tasklet(unsigned long data) */ input = NULL; len = 0; - switch (tp->input->string[0]) { + switch (tp->input[0]) { case AID_ENTER: /* Enter: write input to tty. */ - input = tp->input->string + 6; - len = tp->input->len - 6 - rrq->rescnt; + input = tp->input + 6; + len = tty3270_input_size(tp->view.cols) - 6 - rrq->rescnt; if (tp->inattr != TF_INPUTN) tty3270_rcl_add(tp, input, len); if (tp->nr_up > 0) @@ -685,7 +677,7 @@ static void tty3270_read_tasklet(unsigned long data) (char *)sfq_read_partition, sizeof(sfq_read_partition)); break; case AID_READ_PARTITION: - raw3270_read_modified_cb(tp->readpartreq, tp->input->string); + raw3270_read_modified_cb(tp->readpartreq, tp->input); break; default: break; @@ -698,7 +690,7 @@ static void tty3270_read_tasklet(unsigned long data) while (len-- > 0) kbd_keycode(tp->kbd, *input++); /* Emit keycode for AID byte. */ - kbd_keycode(tp->kbd, 256 + tp->input->string[0]); + kbd_keycode(tp->kbd, 256 + tp->input[0]); raw3270_request_reset(rrq); xchg(&tp->read, rrq); @@ -731,7 +723,7 @@ static void tty3270_issue_read(struct tty3270 *tp, int lock) rrq->callback = tty3270_read_callback; rrq->callback_data = tp; raw3270_request_set_cmd(rrq, TC_READMOD); - raw3270_request_set_data(rrq, tp->input->string, tp->input->len); + raw3270_request_set_data(rrq, tp->input, tty3270_input_size(tp->view.cols)); /* Issue the read modified request. */ if (lock) { rc = raw3270_start(&tp->view, rrq); @@ -938,6 +930,8 @@ static void tty3270_resize(struct raw3270_view *view, { struct tty3270 *tp = container_of(view, struct tty3270, view); struct tty3270_line *screen, *oscreen; + char *old_prompt, *new_prompt; + char *old_input, *new_input; struct tty_struct *tty; struct winsize ws; int new_allocated, old_allocated = tp->allocated_lines; @@ -950,9 +944,17 @@ static void tty3270_resize(struct raw3270_view *view, spin_unlock_irq(&tp->view.lock); return; } + + new_input = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL | GFP_DMA); + if (!new_input) + return; + new_prompt = kzalloc(tty3270_input_size(new_cols), GFP_KERNEL); + if (!new_prompt) + goto out_input; screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated); if (IS_ERR(screen)) - return; + goto out_prompt; + /* Switch to new output size */ spin_lock_irq(&tp->view.lock); tty3270_blank_screen(tp); @@ -962,12 +964,15 @@ static void tty3270_resize(struct raw3270_view *view, tp->view.rows = new_rows; tp->view.cols = new_cols; tp->view.model = new_model; - - free_string(&tp->freemem, tp->prompt); - tty3270_create_prompt(tp); tp->update_flags = TTY_UPDATE_ALL; + old_input = tp->input; + old_prompt = tp->prompt; + tp->input = new_input; + tp->prompt = new_prompt; spin_unlock_irq(&tp->view.lock); tty3270_free_screen(oscreen, old_allocated); + kfree(old_input); + kfree(old_prompt); tty3270_set_timer(tp, 1); /* Informat tty layer about new size */ tty = tty_port_tty_get(&tp->port); @@ -977,6 +982,11 @@ static void tty3270_resize(struct raw3270_view *view, ws.ws_col = tp->view.cols; tty_do_resize(tty, &ws); tty_kref_put(tty); + return; +out_prompt: + kfree(new_prompt); +out_input: + kfree(new_input); } /* @@ -1006,6 +1016,8 @@ static void tty3270_free(struct raw3270_view *view) del_timer_sync(&tp->timer); tty3270_free_screen(tp->screen, tp->allocated_lines); free_page((unsigned long)tp->converted_line); + kfree(tp->input); + kfree(tp->prompt); tty3270_free_view(tp); } @@ -1065,7 +1077,17 @@ tty3270_create_view(int index, struct tty3270 **newtp) goto out_free_screen; } - tty3270_create_prompt(tp); + tp->input = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL | GFP_DMA); + if (!tp->input) { + rc = -ENOMEM; + goto out_free_converted_line; + } + + tp->prompt = kzalloc(tty3270_input_size(tp->view.cols), GFP_KERNEL); + if (!tp->prompt) { + rc = -ENOMEM; + goto out_free_input; + } /* Create blank line for every line in the tty output area. */ tty3270_blank_screen(tp); @@ -1082,6 +1104,10 @@ tty3270_create_view(int index, struct tty3270 **newtp) *newtp = tp; return 0; +out_free_input: + kfree(tp->input); +out_free_converted_line: + free_page((unsigned long)tp->converted_line); out_free_screen: tty3270_free_screen(tp->screen, tp->view.rows); out_put_view: From 76485078702ae680c9683500ad9caafea05678b1 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 28 Nov 2022 11:34:52 +0100 Subject: [PATCH 039/182] s390/con3270: rewrite command line recalling Command line recalling is the last user of the 3270 custom malloc() like allocator. Remove this dependency by using a statically allocated buffer for the saved command lines, and also remove the allocator. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 163 ++++++++++++++++-------------------- drivers/s390/char/raw3270.h | 79 ----------------- 2 files changed, 73 insertions(+), 169 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index e3eee0492fea..71012c20ec7b 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -34,9 +34,8 @@ #define TTY3270_CHAR_BUF_SIZE 256 #define TTY3270_OUTPUT_BUFFER_SIZE 4096 -#define TTY3270_STRING_PAGES 5 - #define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */ +#define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */ static struct tty_driver *tty3270_driver; static int tty3270_max_index; @@ -77,13 +76,9 @@ static const unsigned char sfq_read_partition[] = { struct tty3270 { struct raw3270_view view; struct tty_port port; - void **freemem_pages; /* Array of pages used for freemem. */ - struct list_head freemem; /* List of free memory for strings. */ /* Output stuff. */ - struct list_head lines; /* List of lines. */ unsigned char wcc; /* Write control character. */ - int nr_lines; /* # lines in list. */ int nr_up; /* # lines up in history. */ unsigned long update_flags; /* Update indication bits. */ struct raw3270_request *write; /* Single write request. */ @@ -117,9 +112,9 @@ struct tty3270 { unsigned int saved_cx, saved_cy; /* Command recalling. */ - struct list_head rcl_lines; /* List of recallable lines. */ - struct list_head *rcl_walk; /* Point in rcl_lines list. */ - int rcl_nr, rcl_max; /* Number/max number of rcl_lines. */ + char **rcl_lines; /* Array of recallable lines */ + int rcl_write_index; /* Write index of recallable items */ + int rcl_read_index; /* Read index of recallable items */ /* Character array for put_char/flush_chars. */ unsigned int char_count; @@ -203,10 +198,9 @@ static int tty3270_input_size(int cols) return cols * 2 - 11; } -static void tty3270_update_prompt(struct tty3270 *tp, char *input, int count) +static void tty3270_update_prompt(struct tty3270 *tp, char *input) { - memcpy(tp->prompt, input, count); - tp->prompt[count] = '\0'; + strcpy(tp->prompt, input); tp->update_flags |= TTY_UPDATE_INPUT; tty3270_set_timer(tp, 1); } @@ -271,25 +265,6 @@ static void tty3270_update_string(struct tty3270 *tp, char *line, int len, int n raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1); } -/* - * Alloc string for size bytes. If there is not enough room in - * freemem, free strings until there is room. - */ -static struct string *tty3270_alloc_string(struct tty3270 *tp, size_t size) -{ - struct string *s, *n; - - s = alloc_string(&tp->freemem, size); - if (s) - return s; - list_for_each_entry_safe(s, n, &tp->lines, list) { - list_del(&s->list); - if (free_string(&tp->freemem, s) >= size) - break; - } - return alloc_string(&tp->freemem, size); -} - static void tty3270_blank_screen(struct tty3270 *tp) { struct tty3270_line *line; @@ -542,44 +517,29 @@ static void tty3270_update(struct timer_list *t) */ static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len) { - struct string *s; - - tp->rcl_walk = NULL; + char *p; if (len <= 0) return; - if (tp->rcl_nr >= tp->rcl_max) { - s = list_entry(tp->rcl_lines.next, struct string, list); - list_del(&s->list); - free_string(&tp->freemem, s); - tp->rcl_nr--; - } - s = tty3270_alloc_string(tp, len); - if (!s) - return; - memcpy(s->string, input, len); - list_add_tail(&s->list, &tp->rcl_lines); - tp->rcl_nr++; + p = tp->rcl_lines[tp->rcl_write_index++]; + tp->rcl_write_index &= TTY3270_RECALL_SIZE - 1; + memcpy(p, input, len); + p[len] = '\0'; + tp->rcl_read_index = tp->rcl_write_index; } static void tty3270_rcl_backward(struct kbd_data *kbd) { struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - struct string *s; + int i = 0; spin_lock_irq(&tp->view.lock); if (tp->inattr == TF_INPUT) { - if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) - tp->rcl_walk = tp->rcl_walk->prev; - else if (!list_empty(&tp->rcl_lines)) - tp->rcl_walk = tp->rcl_lines.prev; - s = tp->rcl_walk ? - list_entry(tp->rcl_walk, struct string, list) : NULL; - if (tp->rcl_walk) { - s = list_entry(tp->rcl_walk, struct string, list); - tty3270_update_prompt(tp, s->string, s->len); - } else - tty3270_update_prompt(tp, NULL, 0); - tty3270_set_timer(tp, 1); + do { + tp->rcl_read_index--; + tp->rcl_read_index &= TTY3270_RECALL_SIZE - 1; + } while (!*tp->rcl_lines[tp->rcl_read_index] && + i++ < TTY3270_RECALL_SIZE - 1); + tty3270_update_prompt(tp, tp->rcl_lines[tp->rcl_read_index]); } spin_unlock_irq(&tp->view.lock); } @@ -664,7 +624,7 @@ static void tty3270_read_tasklet(unsigned long data) if (tp->nr_up > 0) tp->nr_up = 0; /* Clear input area. */ - tty3270_update_prompt(tp, NULL, 0); + tty3270_update_prompt(tp, ""); tty3270_set_timer(tp, 1); break; case AID_CLEAR: @@ -796,32 +756,14 @@ static void tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct i static struct tty3270 *tty3270_alloc_view(void) { struct tty3270 *tp; - int pages; tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL); if (!tp) goto out_err; - tp->freemem_pages = - kmalloc_array(TTY3270_STRING_PAGES, sizeof(void *), - GFP_KERNEL); - if (!tp->freemem_pages) - goto out_tp; - INIT_LIST_HEAD(&tp->freemem); - INIT_LIST_HEAD(&tp->lines); - INIT_LIST_HEAD(&tp->rcl_lines); - tp->rcl_max = 20; - for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { - tp->freemem_pages[pages] = (void *) - __get_free_pages(GFP_KERNEL|GFP_DMA, 0); - if (!tp->freemem_pages[pages]) - goto out_pages; - add_string_memory(&tp->freemem, - tp->freemem_pages[pages], PAGE_SIZE); - } tp->write = raw3270_request_alloc(TTY3270_OUTPUT_BUFFER_SIZE); if (IS_ERR(tp->write)) - goto out_pages; + goto out_tp; tp->read = raw3270_request_alloc(0); if (IS_ERR(tp->read)) goto out_write; @@ -851,11 +793,6 @@ static struct tty3270 *tty3270_alloc_view(void) raw3270_request_free(tp->read); out_write: raw3270_request_free(tp->write); -out_pages: - while (pages--) - free_pages((unsigned long) tp->freemem_pages[pages], 0); - kfree(tp->freemem_pages); - tty_port_destroy(&tp->port); out_tp: kfree(tp); out_err: @@ -867,15 +804,10 @@ static struct tty3270 *tty3270_alloc_view(void) */ static void tty3270_free_view(struct tty3270 *tp) { - int pages; - kbd_free(tp->kbd); raw3270_request_free(tp->kreset); raw3270_request_free(tp->read); raw3270_request_free(tp->write); - for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) - free_pages((unsigned long) tp->freemem_pages[pages], 0); - kfree(tp->freemem_pages); free_page((unsigned long)tp->converted_line); tty_port_destroy(&tp->port); kfree(tp); @@ -909,6 +841,38 @@ static struct tty3270_line *tty3270_alloc_screen(struct tty3270 *tp, unsigned in return ERR_PTR(-ENOMEM); } +static char **tty3270_alloc_recall(int cols) +{ + char **lines; + int i; + + lines = kmalloc_array(TTY3270_RECALL_SIZE, sizeof(char *), GFP_KERNEL); + if (!lines) + return NULL; + for (i = 0; i < TTY3270_RECALL_SIZE; i++) { + lines[i] = kcalloc(1, tty3270_input_size(cols) + 1, GFP_KERNEL); + if (!lines[i]) + break; + } + + if (i == TTY3270_RECALL_SIZE) + return lines; + + while (i--) + kfree(lines[i]); + kfree(lines); + return NULL; +} + +static void tty3270_free_recall(char **lines) +{ + int i; + + for (i = 0; i < TTY3270_RECALL_SIZE; i++) + kfree(lines[i]); + kfree(lines); +} + /* * Free tty3270 screen. */ @@ -930,6 +894,7 @@ static void tty3270_resize(struct raw3270_view *view, { struct tty3270 *tp = container_of(view, struct tty3270, view); struct tty3270_line *screen, *oscreen; + char **old_rcl_lines, **new_rcl_lines; char *old_prompt, *new_prompt; char *old_input, *new_input; struct tty_struct *tty; @@ -954,6 +919,9 @@ static void tty3270_resize(struct raw3270_view *view, screen = tty3270_alloc_screen(tp, new_rows, new_cols, &new_allocated); if (IS_ERR(screen)) goto out_prompt; + new_rcl_lines = tty3270_alloc_recall(new_cols); + if (!new_rcl_lines) + goto out_screen; /* Switch to new output size */ spin_lock_irq(&tp->view.lock); @@ -967,12 +935,17 @@ static void tty3270_resize(struct raw3270_view *view, tp->update_flags = TTY_UPDATE_ALL; old_input = tp->input; old_prompt = tp->prompt; + old_rcl_lines = tp->rcl_lines; tp->input = new_input; tp->prompt = new_prompt; + tp->rcl_lines = new_rcl_lines; + tp->rcl_read_index = 0; + tp->rcl_write_index = 0; spin_unlock_irq(&tp->view.lock); tty3270_free_screen(oscreen, old_allocated); kfree(old_input); kfree(old_prompt); + tty3270_free_recall(old_rcl_lines); tty3270_set_timer(tp, 1); /* Informat tty layer about new size */ tty = tty_port_tty_get(&tp->port); @@ -983,6 +956,8 @@ static void tty3270_resize(struct raw3270_view *view, tty_do_resize(tty, &ws); tty_kref_put(tty); return; +out_screen: + tty3270_free_screen(screen, new_rows); out_prompt: kfree(new_prompt); out_input: @@ -1089,6 +1064,12 @@ tty3270_create_view(int index, struct tty3270 **newtp) goto out_free_input; } + tp->rcl_lines = tty3270_alloc_recall(tp->view.cols); + if (!tp->rcl_lines) { + rc = -ENOMEM; + goto out_free_prompt; + } + /* Create blank line for every line in the tty output area. */ tty3270_blank_screen(tp); @@ -1104,6 +1085,8 @@ tty3270_create_view(int index, struct tty3270 **newtp) *newtp = tp; return 0; +out_free_prompt: + kfree(tp->prompt); out_free_input: kfree(tp->input); out_free_converted_line: @@ -1813,7 +1796,7 @@ static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *o new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; if (new != tp->inattr) { tp->inattr = new; - tty3270_update_prompt(tp, NULL, 0); + tty3270_update_prompt(tp, ""); tty3270_set_timer(tp, 1); } } diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 5ca1af6df935..19f2bc1ee72e 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -213,82 +213,3 @@ struct raw3270_notifier { int raw3270_register_notifier(struct raw3270_notifier *); void raw3270_unregister_notifier(struct raw3270_notifier *); -/* - * Little memory allocator for string objects. - */ -struct string -{ - struct list_head list; - unsigned long size; - unsigned long len; - char string[]; -} __attribute__ ((aligned(8))); - -static inline struct string * -alloc_string(struct list_head *free_list, unsigned long len) -{ - struct string *cs, *tmp; - unsigned long size; - - size = (len + 7L) & -8L; - list_for_each_entry(cs, free_list, list) { - if (cs->size < size) - continue; - if (cs->size > size + sizeof(struct string)) { - char *endaddr = (char *) (cs + 1) + cs->size; - tmp = (struct string *) (endaddr - size) - 1; - tmp->size = size; - cs->size -= size + sizeof(struct string); - cs = tmp; - } else - list_del(&cs->list); - cs->len = len; - INIT_LIST_HEAD(&cs->list); - return cs; - } - return NULL; -} - -static inline unsigned long -free_string(struct list_head *free_list, struct string *cs) -{ - struct string *tmp; - struct list_head *p, *left; - - /* Find out the left neighbour in free memory list. */ - left = free_list; - list_for_each(p, free_list) { - if (list_entry(p, struct string, list) > cs) - break; - left = p; - } - /* Try to merge with right neighbour = next element from left. */ - if (left->next != free_list) { - tmp = list_entry(left->next, struct string, list); - if ((char *) (cs + 1) + cs->size == (char *) tmp) { - list_del(&tmp->list); - cs->size += tmp->size + sizeof(struct string); - } - } - /* Try to merge with left neighbour. */ - if (left != free_list) { - tmp = list_entry(left, struct string, list); - if ((char *) (tmp + 1) + tmp->size == (char *) cs) { - tmp->size += cs->size + sizeof(struct string); - return tmp->size; - } - } - __list_add(&cs->list, left, left->next); - return cs->size; -} - -static inline void -add_string_memory(struct list_head *free_list, void *mem, unsigned long size) -{ - struct string *cs; - - cs = (struct string *) mem; - cs->size = size - sizeof(struct string); - free_string(free_list, cs); -} - From 18fc2e93b602d8def2b8d10492238aa40180d292 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 29 Nov 2022 10:31:19 +0100 Subject: [PATCH 040/182] s390/con3270: reduce f_color and b_color attribute size to 4 bit As we're only supportign 8 colors, we don't need 8 bits. Reduce the size to 4 bits to save memory. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 71012c20ec7b..698e0881319a 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -45,8 +45,8 @@ static struct raw3270_fn tty3270_fn; struct tty3270_attribute { unsigned char alternate_charset:1; /* Graphics charset */ unsigned char highlight; /* Blink/reverse/underscore */ - unsigned char f_color; /* Foreground color */ - unsigned char b_color; /* Background color */ + unsigned char f_color:4; /* Foreground color */ + unsigned char b_color:4; /* Background color */ }; struct tty3270_cell { @@ -380,6 +380,18 @@ static char tty3270_graphics_translate(struct tty3270 *tp, char ch) static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line, struct tty3270_attribute *attr, char *cp) { + const unsigned char colors[16] = { + [0] = TAC_DEFAULT, + [1] = TAC_RED, + [2] = TAC_GREEN, + [3] = TAC_YELLOW, + [4] = TAC_BLUE, + [5] = TAC_PINK, + [6] = TAC_TURQ, + [7] = TAC_WHITE, + [9] = TAC_DEFAULT + }; + struct tty3270_cell *cell; int c, i; @@ -392,11 +404,11 @@ static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *lin } if (cell->attributes.f_color != attr->f_color) { attr->f_color = cell->attributes.f_color; - cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, attr->f_color); + cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, colors[attr->f_color]); } if (cell->attributes.b_color != attr->b_color) { attr->b_color = cell->attributes.b_color; - cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, attr->b_color); + cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, colors[attr->b_color]); } c = cell->character; if (cell->attributes.alternate_charset) @@ -1391,10 +1403,6 @@ static void tty3270_erase_display(struct tty3270 *tp, int mode) */ static void tty3270_set_attributes(struct tty3270 *tp) { - static unsigned char colors[] = { - TAC_DEFAULT, TAC_RED, TAC_GREEN, TAC_YELLOW, TAC_BLUE, - TAC_PINK, TAC_TURQ, TAC_WHITE, 0, TAC_DEFAULT - }; int i, attr; for (i = 0; i <= tp->esc_npar; i++) { @@ -1435,7 +1443,7 @@ static void tty3270_set_attributes(struct tty3270 *tp) case 36: /* Cyan */ case 37: /* White */ case 39: /* Black */ - tp->attributes.f_color = colors[attr - 30]; + tp->attributes.f_color = attr - 30; break; /* Background color. */ case 40: /* Black */ @@ -1447,7 +1455,7 @@ static void tty3270_set_attributes(struct tty3270 *tp) case 46: /* Cyan */ case 47: /* White */ case 49: /* Black */ - tp->attributes.b_color = colors[attr - 40]; + tp->attributes.b_color = attr - 40; break; } } From 0573fff2054ea25c463339f38fa6979e85668215 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 29 Nov 2022 11:19:09 +0100 Subject: [PATCH 041/182] s390/con3270: reduce highlight width to 3 bits With the previous change this reduces the size of struct tty3270_attribute from four to two bytes. As we have this struct allocated for each character cell, this saves quite some memory. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 698e0881319a..9ef689c47e0b 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -42,9 +42,13 @@ static int tty3270_max_index; static struct tty3270 *condev; static struct raw3270_fn tty3270_fn; +#define TTY3270_HIGHLIGHT_BLINK 1 +#define TTY3270_HIGHLIGHT_REVERSE 2 +#define TTY3270_HIGHLIGHT_UNDERSCORE 4 + struct tty3270_attribute { unsigned char alternate_charset:1; /* Graphics charset */ - unsigned char highlight; /* Blink/reverse/underscore */ + unsigned char highlight:3; /* Blink/reverse/underscore */ unsigned char f_color:4; /* Foreground color */ unsigned char b_color:4; /* Background color */ }; @@ -301,7 +305,7 @@ static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line int i, flen = 3; /* Prefix (TO_SBA). */ flen += line->len; - highlight = TAX_RESET; + highlight = 0; f_color = TAC_RESET; b_color = TAC_RESET; @@ -321,7 +325,7 @@ static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line if (cell->attributes.alternate_charset) flen += 1; /* TO_GE to switch to graphics extensions */ } - if (highlight != TAX_RESET) + if (highlight) flen += 3; /* TO_SA to reset hightlight. */ if (f_color != TAC_RESET) flen += 3; /* TO_SA to reset color. */ @@ -336,7 +340,7 @@ static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, char *cp, struct tty3270_attribute *attr) { - if (attr->highlight != TAX_RESET) + if (attr->highlight) cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); if (attr->f_color != TAC_RESET) cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAX_RESET); @@ -392,6 +396,12 @@ static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *lin [9] = TAC_DEFAULT }; + const unsigned char highlights[8] = { + [TTY3270_HIGHLIGHT_BLINK] = TAX_BLINK, + [TTY3270_HIGHLIGHT_REVERSE] = TAX_REVER, + [TTY3270_HIGHLIGHT_UNDERSCORE] = TAX_UNDER, + }; + struct tty3270_cell *cell; int c, i; @@ -400,7 +410,7 @@ static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *lin for (i = 0, cell = line->cells; i < line->len; i++, cell++) { if (cell->attributes.highlight != attr->highlight) { attr->highlight = cell->attributes.highlight; - cp = tty3270_add_sa(tp, cp, TAT_EXTHI, attr->highlight); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, highlights[attr->highlight]); } if (cell->attributes.f_color != attr->f_color) { attr->f_color = cell->attributes.f_color; @@ -1413,25 +1423,22 @@ static void tty3270_set_attributes(struct tty3270 *tp) break; /* Highlight. */ case 4: /* Start underlining. */ - tp->attributes.highlight = TAX_UNDER; + tp->attributes.highlight = TTY3270_HIGHLIGHT_UNDERSCORE; break; case 5: /* Start blink. */ - tp->attributes.highlight = TAX_BLINK; + tp->attributes.highlight = TTY3270_HIGHLIGHT_BLINK; break; case 7: /* Start reverse. */ - tp->attributes.highlight = TAX_REVER; + tp->attributes.highlight = TTY3270_HIGHLIGHT_REVERSE; break; case 24: /* End underlining */ - if (tp->attributes.highlight == TAX_UNDER) - tp->attributes.highlight = TAX_RESET; + tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_UNDERSCORE; break; case 25: /* End blink. */ - if (tp->attributes.highlight == TAX_BLINK) - tp->attributes.highlight = TAX_RESET; + tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_BLINK; break; case 27: /* End reverse. */ - if (tp->attributes.highlight == TAX_REVER) - tp->attributes.highlight = TAX_RESET; + tp->attributes.highlight &= ~TTY3270_HIGHLIGHT_REVERSE; break; /* Foreground color. */ case 30: /* Black */ From 525c919d5e1bbe9f5163bc64c04948cf85e88407 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 29 Nov 2022 12:28:36 +0100 Subject: [PATCH 042/182] s390/con3270: add key help to status area To let the user know about function key bindings, print them next to the Running/History field at the lower right of the screen. Also print the scrollback position. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Tested-by: Niklas Schnelle Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 40 ++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 9ef689c47e0b..161c40cfc2f7 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -36,6 +36,7 @@ #define TTY3270_OUTPUT_BUFFER_SIZE 4096 #define TTY3270_SCREEN_PAGES 8 /* has to be power-of-two */ #define TTY3270_RECALL_SIZE 16 /* has to be power-of-two */ +#define TTY3270_STATUS_AREA_SIZE 40 static struct tty_driver *tty3270_driver; static int tty3270_max_index; @@ -223,7 +224,8 @@ static int tty3270_add_prompt(struct tty3270 *tp) if (*tp->prompt) { cp = tty3270_add_sf(tp, cp, TF_INMDT); - count = min_t(int, strlen(tp->prompt), tp->view.cols * 2 - 11); + count = min_t(int, strlen(tp->prompt), + tp->view.cols * 2 - TTY3270_STATUS_AREA_SIZE - 2); memcpy(cp, tp->prompt, count); cp += count; } else { @@ -232,25 +234,49 @@ static int tty3270_add_prompt(struct tty3270 *tp) *cp++ = TO_IC; /* Clear to end of input line. */ if (count < tp->view.cols * 2 - 11) - cp = tty3270_add_ra(tp, cp, -9, -1, 0); + cp = tty3270_add_ra(tp, cp, -TTY3270_STATUS_AREA_SIZE, -1, 0); return cp - tp->converted_line; } +static char *tty3270_ebcdic_convert(struct tty3270 *tp, char *d, char *s) +{ + while (*s) + *d++ = tp->view.ascebc[(int)*s++]; + return d; +} + /* * The status line is the last line of the screen. It shows the string - * "Running"/"Holding" in the lower right corner of the screen. + * "Running"/"History X" in the lower right corner of the screen. */ static int tty3270_add_status(struct tty3270 *tp) { char *cp = tp->converted_line; int len; - cp = tty3270_add_ba(tp, cp, TO_SBA, -9, -1); + cp = tty3270_add_ba(tp, cp, TO_SBA, -TTY3270_STATUS_AREA_SIZE, -1); cp = tty3270_add_sf(tp, cp, TF_LOG); cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_GREEN); - len = sprintf(cp, tp->nr_up ? "History" : "Running"); - codepage_convert(tp->view.ascebc, cp, len); - cp += len; + cp = tty3270_ebcdic_convert(tp, cp, " 7"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); + cp = tty3270_ebcdic_convert(tp, cp, "PrevPg"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + cp = tty3270_ebcdic_convert(tp, cp, " 8"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); + cp = tty3270_ebcdic_convert(tp, cp, "NextPg"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + cp = tty3270_ebcdic_convert(tp, cp, " 12"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_REVER); + cp = tty3270_ebcdic_convert(tp, cp, "Recall"); + cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); + cp = tty3270_ebcdic_convert(tp, cp, " "); + if (tp->nr_up) { + len = sprintf(cp, "History %d", -tp->nr_up); + codepage_convert(tp->view.ascebc, cp, len); + cp += len; + } else { + cp = tty3270_ebcdic_convert(tp, cp, "Running"); + } cp = tty3270_add_sf(tp, cp, TF_LOG); cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET); return cp - (char *)tp->converted_line; From 303bac9df781b42b7c210db269b5b6801f2b9bf0 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 4 Dec 2022 20:52:11 +0100 Subject: [PATCH 043/182] s390/con3270: fix camelcase in enum members fix the following and similar checkpatch warnings: CHECK: Avoid CamelCase: + enum { ESnormal, ESesc, ESsquare, ESparen, ESgetpars }; Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 161c40cfc2f7..f21103ad8321 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1539,22 +1539,22 @@ static void tty3270_goto_xy(struct tty3270 *tp, int cx, int cy) */ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) { - enum { ESnormal, ESesc, ESsquare, ESparen, ESgetpars }; + enum { ES_NORMAL, ES_ESC, ES_SQUARE, ES_PAREN, ES_GETPARS }; - if (tp->esc_state == ESnormal) { + if (tp->esc_state == ES_NORMAL) { if (ch == 0x1b) /* Starting new escape sequence. */ - tp->esc_state = ESesc; + tp->esc_state = ES_ESC; return; } - if (tp->esc_state == ESesc) { - tp->esc_state = ESnormal; + if (tp->esc_state == ES_ESC) { + tp->esc_state = ES_NORMAL; switch (ch) { case '[': - tp->esc_state = ESsquare; + tp->esc_state = ES_SQUARE; break; case '(': - tp->esc_state = ESparen; + tp->esc_state = ES_PAREN; break; case 'E': tty3270_cr(tp); @@ -1590,8 +1590,8 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) } switch (tp->esc_state) { - case ESparen: - tp->esc_state = ESnormal; + case ES_PAREN: + tp->esc_state = ES_NORMAL; switch (ch) { case 'B': tp->attributes.alternate_charset = 0; @@ -1601,15 +1601,15 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) break; } return; - case ESsquare: - tp->esc_state = ESgetpars; + case ES_SQUARE: + tp->esc_state = ES_GETPARS; memset(tp->esc_par, 0, sizeof(tp->esc_par)); tp->esc_npar = 0; tp->esc_ques = (ch == '?'); if (tp->esc_ques) return; fallthrough; - case ESgetpars: + case ES_GETPARS: if (ch == ';' && tp->esc_npar < ESCAPE_NPAR - 1) { tp->esc_npar++; return; @@ -1623,7 +1623,7 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) default: break; } - tp->esc_state = ESnormal; + tp->esc_state = ES_NORMAL; if (ch == 'n' && !tp->esc_ques) { if (tp->esc_par[0] == 5) /* Status report. */ kbd_puts_queue(&tp->port, "\033[0n"); From f8674930891b5d8d8c62246a8d22f096f4b02632 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 4 Dec 2022 20:55:27 +0100 Subject: [PATCH 044/182] s390/con3270: fix multiple assignments in one line fix the following and similar checkpatch warnings: CHECK: multiple assignments should be avoided + tp->cx = tp->saved_cx = 0; Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index f21103ad8321..9a485147b142 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1579,8 +1579,10 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) tp->attributes = tp->saved_attributes; break; case 'c': /* Reset terminal. */ - tp->cx = tp->saved_cx = 0; - tp->cy = tp->saved_cy = 0; + tp->cx = 0; + tp->cy = 0; + tp->saved_cx = 0; + tp->saved_cy = 0; tty3270_reset_attributes(&tp->attributes); tty3270_reset_attributes(&tp->saved_attributes); tty3270_erase_display(tp, 2); @@ -1740,7 +1742,8 @@ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, break; case 0x0c: /* '\f' -- Form Feed */ tty3270_erase_display(tp, 2); - tp->cx = tp->cy = 0; + tp->cx = 0; + tp->cy = 0; break; case 0x0d: /* '\r' -- Carriage Return */ tp->cx = 0; @@ -1883,8 +1886,10 @@ static void tty3270_hangup(struct tty_struct *tty) if (!tp) return; spin_lock_irq(&tp->view.lock); - tp->cx = tp->saved_cx = 0; - tp->cy = tp->saved_cy = 0; + tp->cx = 0; + tp->cy = 0; + tp->saved_cx = 0; + tp->saved_cy = 0; tty3270_reset_attributes(&tp->attributes); tty3270_reset_attributes(&tp->saved_attributes); tty3270_blank_screen(tp); From 9e1d1d8e76625d3689505d56ad2e8af49b37944b Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 4 Dec 2022 20:58:45 +0100 Subject: [PATCH 045/182] s390/con3270: use msecs_to_jiffies() Use msecs_to_jiffies() instead of HZ/10. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 9a485147b142..dce4030d0df6 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -1769,7 +1769,7 @@ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, } /* Setup timer to update display after 1/10 second */ if (!timer_pending(&tp->timer)) - tty3270_set_timer(tp, HZ/10); + tty3270_set_timer(tp, msecs_to_jiffies(100)); spin_unlock_irq(&tp->view.lock); } From 7ef213879a1ea1183b059c15b23c1a20957f4934 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 4 Dec 2022 21:05:37 +0100 Subject: [PATCH 046/182] s390/con3270: fix minor checkpatch issues Fix remaining checkpatch issues, like misplaced brackets, whitespace and similar things. No functional change. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index dce4030d0df6..7d163516ee45 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -289,6 +289,7 @@ static int tty3270_add_status(struct tty3270 *tp) static void tty3270_update_string(struct tty3270 *tp, char *line, int len, int nr) { unsigned char *cp; + raw3270_buffer_address(tp->view.dev, line + 1, 0, nr); cp = line + len - 4; if (*cp == TO_RA) @@ -509,9 +510,9 @@ static void tty3270_update(struct timer_list *t) /* Use erase write alternate to erase display. */ raw3270_request_set_cmd(wrq, TC_EWRITEA); updated |= TTY_UPDATE_ERASE; - } else + } else { raw3270_request_set_cmd(wrq, TC_WRITE); - + } raw3270_request_add_data(wrq, &tp->wcc, 1); tp->wcc = TW_NONE; @@ -566,6 +567,7 @@ static void tty3270_update(struct timer_list *t) static void tty3270_rcl_add(struct tty3270 *tp, char *input, int len) { char *p; + if (len <= 0) return; p = tp->rcl_lines[tp->rcl_write_index++]; @@ -611,6 +613,7 @@ static void tty3270_redraw(struct tty3270 *tp) tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); } + /* * Scroll forward in history. */ @@ -711,6 +714,7 @@ static void tty3270_read_tasklet(unsigned long data) static void tty3270_read_callback(struct raw3270_request *rq, void *data) { struct tty3270 *tp = container_of(rq->view, struct tty3270, view); + raw3270_get_view(rq->view); /* Schedule tasklet to pass input to tty. */ tasklet_schedule(&tp->readlet); @@ -733,9 +737,9 @@ static void tty3270_issue_read(struct tty3270 *tp, int lock) raw3270_request_set_cmd(rrq, TC_READMOD); raw3270_request_set_data(rrq, tp->input, tty3270_input_size(tp->view.cols)); /* Issue the read modified request. */ - if (lock) { + if (lock) rc = raw3270_start(&tp->view, rrq); - } else + else rc = raw3270_start_irq(&tp->view, rrq); if (rc) { raw3270_request_reset(rrq); @@ -749,6 +753,7 @@ static void tty3270_issue_read(struct tty3270 *tp, int lock) static void tty3270_hangup_tasklet(unsigned long data) { struct tty3270 *tp = (struct tty3270 *)data; + tty_port_tty_hangup(&tp->port, true); raw3270_put_view(&tp->view); } @@ -805,7 +810,7 @@ static struct tty3270 *tty3270_alloc_view(void) { struct tty3270 *tp; - tp = kzalloc(sizeof(struct tty3270), GFP_KERNEL); + tp = kzalloc(sizeof(*tp), GFP_KERNEL); if (!tp) goto out_err; @@ -828,9 +833,9 @@ static struct tty3270 *tty3270_alloc_view(void) tty_port_init(&tp->port); timer_setup(&tp->timer, tty3270_update, 0); tasklet_init(&tp->readlet, tty3270_read_tasklet, - (unsigned long) tp->read); + (unsigned long)tp->read); tasklet_init(&tp->hanglet, tty3270_hangup_tasklet, - (unsigned long) tp); + (unsigned long)tp); return tp; out_readpartreq: @@ -1053,6 +1058,7 @@ static void tty3270_del_views(void) for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); + if (!IS_ERR(view)) raw3270_del_view(view); } @@ -1061,7 +1067,7 @@ static void tty3270_del_views(void) static struct raw3270_fn tty3270_fn = { .activate = tty3270_activate, .deactivate = tty3270_deactivate, - .intv = (void *) tty3270_irq, + .intv = (void *)tty3270_irq, .release = tty3270_release, .free = tty3270_free, .resize = tty3270_resize @@ -1631,6 +1637,7 @@ static void tty3270_escape_sequence(struct tty3270 *tp, char ch) kbd_puts_queue(&tp->port, "\033[0n"); else if (tp->esc_par[0] == 6) { /* Cursor report. */ char buf[40]; + sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); kbd_puts_queue(&tp->port, buf); } @@ -1837,7 +1844,7 @@ static void tty3270_set_termios(struct tty_struct *tty, const struct ktermios *o return; spin_lock_irq(&tp->view.lock); if (L_ICANON(tty)) { - new = L_ECHO(tty) ? TF_INPUT: TF_INPUTN; + new = L_ECHO(tty) ? TF_INPUT : TF_INPUTN; if (new != tp->inattr) { tp->inattr = new; tty3270_update_prompt(tp, ""); @@ -1960,8 +1967,7 @@ static void tty3270_destroy_cb(int minor) tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR); } -static struct raw3270_notifier tty3270_notifier = -{ +static struct raw3270_notifier tty3270_notifier = { .create = tty3270_create_cb, .destroy = tty3270_destroy_cb, }; From 754f66b59cc31226601279245356795b7a62e2e3 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 07:56:16 +0100 Subject: [PATCH 047/182] s390/raw3270: move EXPORT_SYMBOL() next to functions Fixes a few checkpatch warning about EXPORT_SYMBOL being at the end of the file instead of being next to the functions. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 6f55a094ad3e..3b6735e13afc 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -30,6 +30,7 @@ #include struct class *class3270; +EXPORT_SYMBOL(class3270); /* The main 3270 data structure. */ struct raw3270 { @@ -91,6 +92,7 @@ module_param(tubxcorrect, bool, 0); * Wait queue for device init/delete, view delete. */ DECLARE_WAIT_QUEUE_HEAD(raw3270_wait_queue); +EXPORT_SYMBOL(raw3270_wait_queue); static void __raw3270_disconnect(struct raw3270 *rp); @@ -130,6 +132,7 @@ void raw3270_buffer_address(struct raw3270 *rp, char *cp, int x, int y) cp[1] = raw3270_ebcgraf[addr & 0x3f]; } } +EXPORT_SYMBOL(raw3270_buffer_address); /* * Allocate a new 3270 ccw request @@ -162,6 +165,7 @@ struct raw3270_request *raw3270_request_alloc(size_t size) return rq; } +EXPORT_SYMBOL(raw3270_request_alloc); /* * Free 3270 ccw request @@ -171,6 +175,7 @@ void raw3270_request_free(struct raw3270_request *rq) kfree(rq->buffer); kfree(rq); } +EXPORT_SYMBOL(raw3270_request_free); /* * Reset request to initial state. @@ -185,6 +190,7 @@ void raw3270_request_reset(struct raw3270_request *rq) rq->rescnt = 0; rq->rc = 0; } +EXPORT_SYMBOL(raw3270_request_reset); /* * Set command code to ccw of a request. @@ -193,6 +199,7 @@ void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd) { rq->ccw.cmd_code = cmd; } +EXPORT_SYMBOL(raw3270_request_set_cmd); /* * Add data fragment to output buffer. @@ -205,6 +212,7 @@ int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size rq->ccw.count += size; return 0; } +EXPORT_SYMBOL(raw3270_request_add_data); /* * Set address/length pair to ccw of a request. @@ -214,6 +222,7 @@ void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t siz rq->ccw.cda = __pa(data); rq->ccw.count = size; } +EXPORT_SYMBOL(raw3270_request_set_data); /* * Set idal buffer to ccw of a request. @@ -224,6 +233,7 @@ void raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib rq->ccw.count = ib->size; rq->ccw.flags |= CCW_FLAG_IDA; } +EXPORT_SYMBOL(raw3270_request_set_idal); /* * Add the request to the request queue, try to start it if the @@ -272,6 +282,7 @@ int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); return rc; } +EXPORT_SYMBOL(raw3270_start); int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, int cmd, void *data, size_t len) @@ -285,6 +296,7 @@ int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, return rc; return raw3270_start(view, rq); } +EXPORT_SYMBOL(raw3270_start_request); int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) { @@ -300,6 +312,7 @@ int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) rc = __raw3270_start(rp, view, rq); return rc; } +EXPORT_SYMBOL(raw3270_start_locked); int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) { @@ -311,6 +324,7 @@ int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq) list_add_tail(&rq->list, &rp->req_queue); return 0; } +EXPORT_SYMBOL(raw3270_start_irq); /* * 3270 interrupt routine, called from the ccw_device layer @@ -546,6 +560,7 @@ void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) raw3270_size_device(rp, data); raw3270_size_device_done(rp); } +EXPORT_SYMBOL(raw3270_read_modified_cb); static void raw3270_read_modified(struct raw3270 *rp) { @@ -646,6 +661,7 @@ int raw3270_reset(struct raw3270_view *view) rc = raw3270_reset_device(view->dev); return rc; } +EXPORT_SYMBOL(raw3270_reset); static void __raw3270_disconnect(struct raw3270 *rp) { @@ -907,6 +923,7 @@ int raw3270_activate_view(struct raw3270_view *view) spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rc; } +EXPORT_SYMBOL(raw3270_activate_view); /* * Deactivate current view. @@ -938,6 +955,7 @@ void raw3270_deactivate_view(struct raw3270_view *view) } spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); } +EXPORT_SYMBOL(raw3270_deactivate_view); /* * Add view to device with minor "minor". @@ -974,6 +992,7 @@ int raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, mutex_unlock(&raw3270_mutex); return rc; } +EXPORT_SYMBOL(raw3270_add_view); /* * Find specific view of device with minor "minor". @@ -1003,6 +1022,7 @@ struct raw3270_view *raw3270_find_view(struct raw3270_fn *fn, int minor) mutex_unlock(&raw3270_mutex); return view; } +EXPORT_SYMBOL(raw3270_find_view); /* * Remove view from device and free view structure via call to view->fn->free. @@ -1036,6 +1056,7 @@ void raw3270_del_view(struct raw3270_view *view) if (view->fn->free) view->fn->free(view); } +EXPORT_SYMBOL(raw3270_del_view); /* * Remove a 3270 device structure. @@ -1128,6 +1149,7 @@ int raw3270_register_notifier(struct raw3270_notifier *notifier) mutex_unlock(&raw3270_mutex); return 0; } +EXPORT_SYMBOL(raw3270_register_notifier); void raw3270_unregister_notifier(struct raw3270_notifier *notifier) { @@ -1139,6 +1161,7 @@ void raw3270_unregister_notifier(struct raw3270_notifier *notifier) list_del(¬ifier->list); mutex_unlock(&raw3270_mutex); } +EXPORT_SYMBOL(raw3270_unregister_notifier); /* * Set 3270 device online. @@ -1291,27 +1314,3 @@ MODULE_LICENSE("GPL"); module_init(raw3270_init); module_exit(raw3270_exit); - -EXPORT_SYMBOL(class3270); -EXPORT_SYMBOL(raw3270_read_modified_cb); -EXPORT_SYMBOL(raw3270_request_alloc); -EXPORT_SYMBOL(raw3270_request_free); -EXPORT_SYMBOL(raw3270_request_reset); -EXPORT_SYMBOL(raw3270_request_set_cmd); -EXPORT_SYMBOL(raw3270_request_add_data); -EXPORT_SYMBOL(raw3270_request_set_data); -EXPORT_SYMBOL(raw3270_request_set_idal); -EXPORT_SYMBOL(raw3270_buffer_address); -EXPORT_SYMBOL(raw3270_add_view); -EXPORT_SYMBOL(raw3270_del_view); -EXPORT_SYMBOL(raw3270_find_view); -EXPORT_SYMBOL(raw3270_activate_view); -EXPORT_SYMBOL(raw3270_deactivate_view); -EXPORT_SYMBOL(raw3270_start); -EXPORT_SYMBOL(raw3270_start_request); -EXPORT_SYMBOL(raw3270_start_locked); -EXPORT_SYMBOL(raw3270_start_irq); -EXPORT_SYMBOL(raw3270_reset); -EXPORT_SYMBOL(raw3270_register_notifier); -EXPORT_SYMBOL(raw3270_unregister_notifier); -EXPORT_SYMBOL(raw3270_wait_queue); From ff61744c97ffd7e91967114f366779d600653684 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 08:40:00 +0100 Subject: [PATCH 048/182] s390/raw3270: fix indentation/whitespace errors Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 39 ++++++++++++++++++++----------------- drivers/s390/char/raw3270.h | 4 ++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 3b6735e13afc..1c21addfe0e1 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -142,7 +142,7 @@ struct raw3270_request *raw3270_request_alloc(size_t size) struct raw3270_request *rq; /* Allocate request structure */ - rq = kzalloc(sizeof(struct raw3270_request), GFP_KERNEL | GFP_DMA); + rq = kzalloc(sizeof(*rq), GFP_KERNEL | GFP_DMA); if (!rq) return ERR_PTR(-ENOMEM); @@ -248,7 +248,7 @@ static int __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, !test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { /* No other requests are on the queue. Start this one. */ rq->rc = ccw_device_start(rp->cdev, &rq->ccw, - (unsigned long) rq, 0, 0); + (unsigned long)rq, 0, 0); if (rq->rc) { raw3270_put_view(view); return rq->rc; @@ -338,7 +338,7 @@ static void raw3270_irq(struct ccw_device *cdev, unsigned long intparm, struct i rp = dev_get_drvdata(&cdev->dev); if (!rp) return; - rq = (struct raw3270_request *) intparm; + rq = (struct raw3270_request *)intparm; view = rq ? rq->view : rp->view; if (!IS_ERR(irb)) { @@ -379,9 +379,9 @@ static void raw3270_irq(struct ccw_device *cdev, unsigned long intparm, struct i * started successful. */ while (!list_empty(&rp->req_queue)) { - rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + rq = list_entry(rp->req_queue.next, struct raw3270_request, list); rq->rc = ccw_device_start(rp->cdev, &rq->ccw, - (unsigned long) rq, 0, 0); + (unsigned long)rq, 0, 0); if (rq->rc == 0) break; /* Start failed. Remove request and do callback. */ @@ -557,6 +557,7 @@ static void raw3270_size_device_done(struct raw3270 *rp) void raw3270_read_modified_cb(struct raw3270_request *rq, void *data) { struct raw3270 *rp = rq->view->dev; + raw3270_size_device(rp, data); raw3270_size_device_done(rp); } @@ -572,7 +573,7 @@ static void raw3270_read_modified(struct raw3270 *rp) rp->init_readmod.ccw.cmd_code = TC_READMOD; rp->init_readmod.ccw.flags = CCW_FLAG_SLI; rp->init_readmod.ccw.count = sizeof(rp->init_data); - rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_readmod.ccw.cda = (__u32)__pa(rp->init_data); rp->init_readmod.callback = raw3270_read_modified_cb; rp->init_readmod.callback_data = rp->init_data; rp->state = RAW3270_STATE_READMOD; @@ -581,8 +582,9 @@ static void raw3270_read_modified(struct raw3270 *rp) static void raw3270_writesf_readpart(struct raw3270 *rp) { - static const unsigned char wbuf[] = - { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; + static const unsigned char wbuf[] = { + 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 + }; /* Store 'read partition' data stream to init_data */ memset(&rp->init_readpart, 0, sizeof(rp->init_readpart)); @@ -591,7 +593,7 @@ static void raw3270_writesf_readpart(struct raw3270 *rp) rp->init_readpart.ccw.cmd_code = TC_WRITESF; rp->init_readpart.ccw.flags = CCW_FLAG_SLI; rp->init_readpart.ccw.count = sizeof(wbuf); - rp->init_readpart.ccw.cda = (__u32) __pa(&rp->init_data); + rp->init_readpart.ccw.cda = (__u32)__pa(&rp->init_data); rp->state = RAW3270_STATE_W4ATTN; raw3270_start_irq(&rp->init_view, &rp->init_readpart); } @@ -611,8 +613,9 @@ static void raw3270_reset_device_cb(struct raw3270_request *rq, void *data) } else if (MACHINE_IS_VM) { raw3270_size_device_vm(rp); raw3270_size_device_done(rp); - } else + } else { raw3270_writesf_readpart(rp); + } memset(&rp->init_reset, 0, sizeof(rp->init_reset)); } @@ -628,7 +631,7 @@ static int __raw3270_reset_device(struct raw3270 *rp) rp->init_reset.ccw.cmd_code = TC_EWRITEA; rp->init_reset.ccw.flags = CCW_FLAG_SLI; rp->init_reset.ccw.count = 1; - rp->init_reset.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_reset.ccw.cda = (__u32)__pa(rp->init_data); rp->init_reset.callback = raw3270_reset_device_cb; rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset); if (rc == 0 && rp->state == RAW3270_STATE_INIT) @@ -672,7 +675,7 @@ static void __raw3270_disconnect(struct raw3270 *rp) rp->view = &rp->init_view; /* Cancel all queued requests */ while (!list_empty(&rp->req_queue)) { - rq = list_entry(rp->req_queue.next,struct raw3270_request,list); + rq = list_entry(rp->req_queue.next, struct raw3270_request, list); view = rq->view; rq->rc = -EACCES; list_del_init(&rq->list); @@ -800,7 +803,7 @@ struct raw3270 __init *raw3270_setup_console(void) if (IS_ERR(cdev)) return ERR_CAST(cdev); - rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); + rp = kzalloc(sizeof(*rp), GFP_KERNEL | GFP_DMA); ascebc = kzalloc(256, GFP_KERNEL); rc = raw3270_setup_device(cdev, rp, ascebc); if (rc) @@ -845,7 +848,7 @@ static struct raw3270 *raw3270_create_device(struct ccw_device *cdev) char *ascebc; int rc; - rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); + rp = kzalloc(sizeof(*rp), GFP_KERNEL | GFP_DMA); if (!rp) return ERR_PTR(-ENOMEM); ascebc = kmalloc(256, GFP_KERNEL); @@ -893,11 +896,11 @@ int raw3270_activate_view(struct raw3270_view *view) if (!rp) return -ENODEV; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (rp->view == view) + if (rp->view == view) { rc = 0; - else if (!raw3270_state_ready(rp)) + } else if (!raw3270_state_ready(rp)) { rc = -EBUSY; - else { + } else { oldview = NULL; if (rp->view && rp->view->fn->deactivate) { oldview = rp->view; @@ -1117,7 +1120,7 @@ raw3270_columns_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(columns, 0444, raw3270_columns_show, NULL); -static struct attribute * raw3270_attrs[] = { +static struct attribute *raw3270_attrs[] = { &dev_attr_model.attr, &dev_attr_rows.attr, &dev_attr_columns.attr, diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 19f2bc1ee72e..c64eac3f056c 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -16,8 +16,8 @@ #define TUBOCMD _IO('3', 4) /* set ccw command for fs writes. */ #define TUBGETI _IO('3', 7) /* get ccw command for fs reads. */ #define TUBGETO _IO('3', 8) /* get ccw command for fs writes. */ -#define TUBSETMOD _IO('3',12) /* FIXME: what does it do ?*/ -#define TUBGETMOD _IO('3',13) /* FIXME: what does it do ?*/ +#define TUBSETMOD _IO('3', 12) /* FIXME: what does it do ?*/ +#define TUBGETMOD _IO('3', 13) /* FIXME: what does it do ?*/ /* Local Channel Commands */ #define TC_WRITE 0x01 /* Write */ From 0d85d8edaf302f070da0ba838fdefe8afa3ef681 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 08:52:29 +0100 Subject: [PATCH 049/182] s390/raw3270: fix raw3270 declarations checkpatch complains about missing argument names in function declarations. Fix it. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.h | 59 +++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index c64eac3f056c..449eae127b72 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -114,17 +114,17 @@ struct raw3270_request { int rc; /* return code for this request. */ /* Callback for delivering final status. */ - void (*callback)(struct raw3270_request *, void *); + void (*callback)(struct raw3270_request *rq, void *data); void *callback_data; }; struct raw3270_request *raw3270_request_alloc(size_t size); -void raw3270_request_free(struct raw3270_request *); -void raw3270_request_reset(struct raw3270_request *); -void raw3270_request_set_cmd(struct raw3270_request *, u8 cmd); -int raw3270_request_add_data(struct raw3270_request *, void *, size_t); -void raw3270_request_set_data(struct raw3270_request *, void *, size_t); -void raw3270_request_set_idal(struct raw3270_request *, struct idal_buffer *); +void raw3270_request_free(struct raw3270_request *rq); +void raw3270_request_reset(struct raw3270_request *rq); +void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd); +int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size); +void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size); +void raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib); static inline int raw3270_request_final(struct raw3270_request *rq) @@ -138,13 +138,15 @@ void raw3270_buffer_address(struct raw3270 *, char *, int, int); * Functions of a 3270 view. */ struct raw3270_fn { - int (*activate)(struct raw3270_view *); - void (*deactivate)(struct raw3270_view *); - void (*intv)(struct raw3270_view *, - struct raw3270_request *, struct irb *); - void (*release)(struct raw3270_view *); - void (*free)(struct raw3270_view *); - void (*resize)(struct raw3270_view *, int, int, int, int, int, int); + int (*activate)(struct raw3270_view *rq); + void (*deactivate)(struct raw3270_view *rq); + void (*intv)(struct raw3270_view *view, + struct raw3270_request *rq, struct irb *ib); + void (*release)(struct raw3270_view *view); + void (*free)(struct raw3270_view *view); + void (*resize)(struct raw3270_view *view, + int new_model, int new_cols, int new_rows, + int old_model, int old_cols, int old_rows); }; /* @@ -168,18 +170,18 @@ struct raw3270_view { unsigned char *ascebc; /* ascii -> ebcdic table */ }; -int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int, int); +int raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, int subclass); int raw3270_view_lock_unavailable(struct raw3270_view *view); -int raw3270_activate_view(struct raw3270_view *); -void raw3270_del_view(struct raw3270_view *); -void raw3270_deactivate_view(struct raw3270_view *); -struct raw3270_view *raw3270_find_view(struct raw3270_fn *, int); -int raw3270_start(struct raw3270_view *, struct raw3270_request *); -int raw3270_start_locked(struct raw3270_view *, struct raw3270_request *); -int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *); -int raw3270_reset(struct raw3270_view *); -struct raw3270_view *raw3270_view(struct raw3270_view *); -int raw3270_view_active(struct raw3270_view *); +int raw3270_activate_view(struct raw3270_view *view); +void raw3270_del_view(struct raw3270_view *view); +void raw3270_deactivate_view(struct raw3270_view *view); +struct raw3270_view *raw3270_find_view(struct raw3270_fn *fn, int minor); +int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq); +int raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq); +int raw3270_start_irq(struct raw3270_view *view, struct raw3270_request *rq); +int raw3270_reset(struct raw3270_view *view); +struct raw3270_view *raw3270_view(struct raw3270_view *view); +int raw3270_view_active(struct raw3270_view *view); int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, int cmd, void *data, size_t len); void raw3270_read_modified_cb(struct raw3270_request *rq, void *data); @@ -201,7 +203,7 @@ raw3270_put_view(struct raw3270_view *view) } struct raw3270 *raw3270_setup_console(void); -void raw3270_wait_cons_dev(struct raw3270 *); +void raw3270_wait_cons_dev(struct raw3270 *rp); /* Notifier for device addition/removal */ struct raw3270_notifier { @@ -210,6 +212,5 @@ struct raw3270_notifier { void (*destroy)(int minor); }; -int raw3270_register_notifier(struct raw3270_notifier *); -void raw3270_unregister_notifier(struct raw3270_notifier *); - +int raw3270_register_notifier(struct raw3270_notifier *notifier); +void raw3270_unregister_notifier(struct raw3270_notifier *notifier); From fd2a41d07b2f8be0f490f8f78280e574eb9bda5a Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 08:57:01 +0100 Subject: [PATCH 050/182] s390/raw3270: add comment to spinlock member Add a small comment to the lock member of struct raw3270_view to make checkpatch happy. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 449eae127b72..47b41778faae 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -159,7 +159,7 @@ struct raw3270_fn { */ struct raw3270_view { struct list_head list; - spinlock_t lock; + spinlock_t lock; /* protects members of view */ #define RAW3270_VIEW_LOCK_IRQ 0 #define RAW3270_VIEW_LOCK_BH 1 atomic_t ref_count; From 82df96d849147c72013639134de21296b4b19d83 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 09:02:16 +0100 Subject: [PATCH 051/182] s390/raw3270: use DEVICE_ATTR_RO() for sysfs attributes Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 1c21addfe0e1..130295fd1253 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1095,30 +1095,30 @@ static int raw3270_probe(struct ccw_device *cdev) /* * Additional attributes for a 3270 device */ -static ssize_t raw3270_model_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->model); } -static DEVICE_ATTR(model, 0444, raw3270_model_show, NULL); +static DEVICE_ATTR_RO(model); -static ssize_t raw3270_rows_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t rows_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->rows); } -static DEVICE_ATTR(rows, 0444, raw3270_rows_show, NULL); +static DEVICE_ATTR_RO(rows); static ssize_t -raw3270_columns_show(struct device *dev, struct device_attribute *attr, - char *buf) +columns_show(struct device *dev, struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%i\n", ((struct raw3270 *)dev_get_drvdata(dev))->cols); } -static DEVICE_ATTR(columns, 0444, raw3270_columns_show, NULL); +static DEVICE_ATTR_RO(columns); static struct attribute *raw3270_attrs[] = { &dev_attr_model.attr, From 7aeeeb926c355a9a5bc7a96bae7d5ada44f1d2e9 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 09:09:33 +0100 Subject: [PATCH 052/182] s390/raw3270: remove BUG_ON in raw3270_request_reset() WARN_ON_ONCE if list is not empty, and return an error code instead. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 10 +++++++--- drivers/s390/char/raw3270.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 130295fd1253..2939034431c0 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -180,15 +180,17 @@ EXPORT_SYMBOL(raw3270_request_free); /* * Reset request to initial state. */ -void raw3270_request_reset(struct raw3270_request *rq) +int raw3270_request_reset(struct raw3270_request *rq) { - BUG_ON(!list_empty(&rq->list)); + if (WARN_ON_ONCE(!list_empty(&rq->list))) + return -EBUSY; rq->ccw.cmd_code = 0; rq->ccw.count = 0; rq->ccw.cda = __pa(rq->buffer); rq->ccw.flags = CCW_FLAG_SLI; rq->rescnt = 0; rq->rc = 0; + return 0; } EXPORT_SYMBOL(raw3270_request_reset); @@ -289,7 +291,9 @@ int raw3270_start_request(struct raw3270_view *view, struct raw3270_request *rq, { int rc; - raw3270_request_reset(rq); + rc = raw3270_request_reset(rq); + if (rc) + return rc; raw3270_request_set_cmd(rq, cmd); rc = raw3270_request_add_data(rq, data, len); if (rc) diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 47b41778faae..e8c71ef7bfa0 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -120,7 +120,7 @@ struct raw3270_request { struct raw3270_request *raw3270_request_alloc(size_t size); void raw3270_request_free(struct raw3270_request *rq); -void raw3270_request_reset(struct raw3270_request *rq); +int raw3270_request_reset(struct raw3270_request *rq); void raw3270_request_set_cmd(struct raw3270_request *rq, u8 cmd); int raw3270_request_add_data(struct raw3270_request *rq, void *data, size_t size); void raw3270_request_set_data(struct raw3270_request *rq, void *data, size_t size); From 420105f4506be36c5b1fcb1f0b5e8fe03f962238 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 09:46:24 +0100 Subject: [PATCH 053/182] s390/raw3270: split up raw3270_activate_view() move the core processing to __raw3270_activate_view() to reduce the required if/else blocks and indentiion levels. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 74 +++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 2939034431c0..0724a1ab117a 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -886,13 +886,57 @@ int raw3270_view_lock_unavailable(struct raw3270_view *view) return 0; } +static int raw3270_assign_activate_view(struct raw3270 *rp, struct raw3270_view *view) +{ + rp->view = view; + return view->fn->activate(view); +} + +static int __raw3270_activate_view(struct raw3270 *rp, struct raw3270_view *view) +{ + struct raw3270_view *oldview = NULL, *nv; + int rc; + + if (rp->view == view) + return 0; + + if (!raw3270_state_ready(rp)) + return -EBUSY; + + if (rp->view && rp->view->fn->deactivate) { + oldview = rp->view; + oldview->fn->deactivate(oldview); + } + + rc = raw3270_assign_activate_view(rp, view); + if (!rc) + return 0; + + /* Didn't work. Try to reactivate the old view. */ + if (oldview) { + rc = raw3270_assign_activate_view(rp, oldview); + if (!rc) + return 0; + } + + /* Didn't work as well. Try any other view. */ + list_for_each_entry(nv, &rp->view_list, list) { + if (nv == view || nv == oldview) + continue; + rc = raw3270_assign_activate_view(rp, nv); + if (!rc) + break; + rp->view = NULL; + } + return rc; +} + /* * Activate a view. */ int raw3270_activate_view(struct raw3270_view *view) { struct raw3270 *rp; - struct raw3270_view *oldview, *nv; unsigned long flags; int rc; @@ -900,33 +944,7 @@ int raw3270_activate_view(struct raw3270_view *view) if (!rp) return -ENODEV; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (rp->view == view) { - rc = 0; - } else if (!raw3270_state_ready(rp)) { - rc = -EBUSY; - } else { - oldview = NULL; - if (rp->view && rp->view->fn->deactivate) { - oldview = rp->view; - oldview->fn->deactivate(oldview); - } - rp->view = view; - rc = view->fn->activate(view); - if (rc) { - /* Didn't work. Try to reactivate the old view. */ - rp->view = oldview; - if (!oldview || oldview->fn->activate(oldview) != 0) { - /* Didn't work as well. Try any other view. */ - list_for_each_entry(nv, &rp->view_list, list) - if (nv != view && nv != oldview) { - rp->view = nv; - if (nv->fn->activate(nv) == 0) - break; - rp->view = NULL; - } - } - } - } + rc = __raw3270_activate_view(rp, view); spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rc; } From 31bc23241b54b23fb880db6f5e16a443ab44e5d0 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 10:01:38 +0100 Subject: [PATCH 054/182] s390/raw3270: fix nullpointer check Fix the following checkpatch warning: CHECK: Comparison to NULL could be written "!rp" + if (rp == NULL) Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/raw3270.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 0724a1ab117a..09d7570d3b7d 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1232,7 +1232,7 @@ static void raw3270_remove(struct ccw_device *cdev) * devices even if they haven't been varied online. * Thus, rp may validly be NULL here. */ - if (rp == NULL) + if (!rp) return; sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group); From a82603b0d6ee393046e4d53f8708e02e12cf8a82 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 10:27:20 +0100 Subject: [PATCH 055/182] s390/fs3270: fix whitespace errors Fix a few whitespace errors reported by checkpatch, namely superfluous whitespace, missing spaces and empty lines. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/fs3270.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 4c4683d8784a..c354a294d59a 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -47,7 +47,7 @@ static DEFINE_MUTEX(fs3270_mutex); static void fs3270_wake_up(struct raw3270_request *rq, void *data) { - wake_up((wait_queue_head_t *) data); + wake_up((wait_queue_head_t *)data); } static inline int @@ -66,7 +66,7 @@ fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) struct fs3270 *fp; int rc; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; rq->callback = fs3270_wake_up; rq->callback_data = &fp->wait; @@ -95,7 +95,7 @@ fs3270_reset_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; - fp = (struct fs3270 *) rq->view; + fp = (struct fs3270 *)rq->view; raw3270_request_reset(rq); wake_up(&fp->wait); } @@ -105,7 +105,7 @@ fs3270_restore_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; - fp = (struct fs3270 *) rq->view; + fp = (struct fs3270 *)rq->view; if (rq->rc != 0 || rq->rescnt != 0) { if (fp->fs_pid) kill_pid(fp->fs_pid, SIGHUP, 1); @@ -122,7 +122,7 @@ fs3270_activate(struct raw3270_view *view) char *cp; int rc; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; /* If an old init command is still running just return. */ if (!raw3270_request_final(fp->init)) @@ -165,7 +165,7 @@ fs3270_save_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; - fp = (struct fs3270 *) rq->view; + fp = (struct fs3270 *)rq->view; /* Correct idal buffer element 0 address. */ fp->rdbuf->data[0] -= 5; @@ -192,7 +192,7 @@ fs3270_deactivate(struct raw3270_view *view) { struct fs3270 *fp; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; fp->active = 0; /* If an old init command is still running just return. */ @@ -246,7 +246,7 @@ fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) struct raw3270_request *rq; struct idal_buffer *ib; ssize_t rc; - + if (count == 0 || count > 65535) return -EINVAL; fp = filp->private_data; @@ -271,7 +271,6 @@ fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) rc = -EFAULT; else rc = count; - } } raw3270_request_free(rq); @@ -375,7 +374,7 @@ fs3270_alloc_view(void) { struct fs3270 *fp; - fp = kzalloc(sizeof(struct fs3270),GFP_KERNEL); + fp = kzalloc(sizeof(struct fs3270), GFP_KERNEL); if (!fp) return ERR_PTR(-ENOMEM); fp->init = raw3270_request_alloc(0); @@ -394,10 +393,10 @@ fs3270_free_view(struct raw3270_view *view) { struct fs3270 *fp; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; if (fp->rdbuf) idal_buffer_free(fp->rdbuf); - raw3270_request_free(((struct fs3270 *) view)->init); + raw3270_request_free(((struct fs3270 *)view)->init); kfree(view); } @@ -409,7 +408,7 @@ fs3270_release(struct raw3270_view *view) { struct fs3270 *fp; - fp = (struct fs3270 *) view; + fp = (struct fs3270 *)view; if (fp->fs_pid) kill_pid(fp->fs_pid, SIGHUP, 1); } @@ -418,7 +417,7 @@ fs3270_release(struct raw3270_view *view) static struct raw3270_fn fs3270_fn = { .activate = fs3270_activate, .deactivate = fs3270_deactivate, - .intv = (void *) fs3270_irq, + .intv = (void *)fs3270_irq, .release = fs3270_release, .free = fs3270_free_view }; @@ -439,6 +438,7 @@ fs3270_open(struct inode *inode, struct file *filp) /* Check for minor 0 multiplexer. */ if (minor == 0) { struct tty_struct *tty = get_current_tty(); + if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { tty_kref_put(tty); return -ENODEV; @@ -448,7 +448,7 @@ fs3270_open(struct inode *inode, struct file *filp) } mutex_lock(&fs3270_mutex); /* Check if some other program is already using fullscreen mode. */ - fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); + fp = (struct fs3270 *)raw3270_find_view(&fs3270_fn, minor); if (!IS_ERR(fp)) { raw3270_put_view(&fp->view); rc = -EBUSY; @@ -471,7 +471,7 @@ fs3270_open(struct inode *inode, struct file *filp) } /* Allocate idal-buffer. */ - ib = idal_buffer_alloc(2*fp->view.rows*fp->view.cols + 5, 0); + ib = idal_buffer_alloc(2 * fp->view.rows * fp->view.cols + 5, 0); if (IS_ERR(ib)) { raw3270_put_view(&fp->view); raw3270_del_view(&fp->view); From 945775155e21097cacb60ed182f7eb348bb8ef24 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 10:29:05 +0100 Subject: [PATCH 056/182] s390/fs3270: add missing braces to if/else Fix a few missing braces and wrong placement of braces reported by checkpatch. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/fs3270.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index c354a294d59a..1b7df920ae10 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -181,8 +181,9 @@ fs3270_save_callback(struct raw3270_request *rq, void *data) if (fp->fs_pid) kill_pid(fp->fs_pid, SIGHUP, 1); fp->rdbuf_size = 0; - } else + } else { fp->rdbuf_size = fp->rdbuf->size - rq->rescnt; + } raw3270_request_reset(rq); wake_up(&fp->wait); } @@ -274,8 +275,9 @@ fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) } } raw3270_request_free(rq); - } else + } else { rc = PTR_ERR(rq); + } idal_buffer_free(ib); return rc; } @@ -309,11 +311,13 @@ fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *o rc = fs3270_do_io(&fp->view, rq); if (rc == 0) rc = count - rq->rescnt; - } else + } else { rc = -EFAULT; + } raw3270_request_free(rq); - } else + } else { rc = PTR_ERR(rq); + } idal_buffer_free(ib); return rc; } @@ -538,8 +542,7 @@ static void fs3270_destroy_cb(int minor) __unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); } -static struct raw3270_notifier fs3270_notifier = -{ +static struct raw3270_notifier fs3270_notifier = { .create = fs3270_create_cb, .destroy = fs3270_destroy_cb, }; From aa08b6a46b6060cab0de44f1b1db58fcc867b60f Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 10:30:48 +0100 Subject: [PATCH 057/182] s390/fs3270: remove duplicate assignment remove a duplicate assignment reported by checkpatch. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/fs3270.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 1b7df920ae10..836c09a47860 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -149,7 +149,8 @@ fs3270_activate(struct raw3270_view *view) fp->init->rescnt = 0; fp->init->callback = fs3270_restore_callback; } - rc = fp->init->rc = raw3270_start_locked(view, fp->init); + rc = raw3270_start_locked(view, fp->init); + fp->init->rc = rc; if (rc) fp->init->callback(fp->init, NULL); else From 84a8b601eac5316dcd01fb9cdd9b28261225c663 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 10:31:56 +0100 Subject: [PATCH 058/182] s390/fs3270: use *ptr instead of struct in kzalloc Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/fs3270.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 836c09a47860..32fdb0241cf3 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -379,7 +379,7 @@ fs3270_alloc_view(void) { struct fs3270 *fp; - fp = kzalloc(sizeof(struct fs3270), GFP_KERNEL); + fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (!fp) return ERR_PTR(-ENOMEM); fp->init = raw3270_request_alloc(0); From ec40213bfbe41a73f8b9b96643c66edb0cbeb064 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 10:38:39 +0100 Subject: [PATCH 059/182] s390/fs3270: fix function prototypes fix function prototypes split over two lines like: static void foobar(void) Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/fs3270.c | 60 ++++++++++++++------------------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 32fdb0241cf3..7975dbea8d1b 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -44,14 +44,12 @@ struct fs3270 { static DEFINE_MUTEX(fs3270_mutex); -static void -fs3270_wake_up(struct raw3270_request *rq, void *data) +static void fs3270_wake_up(struct raw3270_request *rq, void *data) { wake_up((wait_queue_head_t *)data); } -static inline int -fs3270_working(struct fs3270 *fp) +static inline int fs3270_working(struct fs3270 *fp) { /* * The fullscreen view is in working order if the view @@ -60,8 +58,7 @@ fs3270_working(struct fs3270 *fp) return fp->active && raw3270_request_final(fp->init); } -static int -fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) +static int fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) { struct fs3270 *fp; int rc; @@ -90,8 +87,7 @@ fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq) /* * Switch to the fullscreen view. */ -static void -fs3270_reset_callback(struct raw3270_request *rq, void *data) +static void fs3270_reset_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; @@ -100,8 +96,7 @@ fs3270_reset_callback(struct raw3270_request *rq, void *data) wake_up(&fp->wait); } -static void -fs3270_restore_callback(struct raw3270_request *rq, void *data) +static void fs3270_restore_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; @@ -115,8 +110,7 @@ fs3270_restore_callback(struct raw3270_request *rq, void *data) wake_up(&fp->wait); } -static int -fs3270_activate(struct raw3270_view *view) +static int fs3270_activate(struct raw3270_view *view) { struct fs3270 *fp; char *cp; @@ -161,8 +155,7 @@ fs3270_activate(struct raw3270_view *view) /* * Shutdown fullscreen view. */ -static void -fs3270_save_callback(struct raw3270_request *rq, void *data) +static void fs3270_save_callback(struct raw3270_request *rq, void *data) { struct fs3270 *fp; @@ -189,8 +182,7 @@ fs3270_save_callback(struct raw3270_request *rq, void *data) wake_up(&fp->wait); } -static void -fs3270_deactivate(struct raw3270_view *view) +static void fs3270_deactivate(struct raw3270_view *view) { struct fs3270 *fp; @@ -220,8 +212,8 @@ fs3270_deactivate(struct raw3270_view *view) fp->init->callback(fp->init, NULL); } -static void -fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) +static void fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, + struct irb *irb) { /* Handle ATTN. Set indication and wake waiters for attention. */ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { @@ -241,8 +233,8 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) /* * Process reads from fullscreen 3270. */ -static ssize_t -fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) +static ssize_t fs3270_read(struct file *filp, char __user *data, + size_t count, loff_t *off) { struct fs3270 *fp; struct raw3270_request *rq; @@ -286,8 +278,8 @@ fs3270_read(struct file *filp, char __user *data, size_t count, loff_t *off) /* * Process writes to fullscreen 3270. */ -static ssize_t -fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *off) +static ssize_t fs3270_write(struct file *filp, const char __user *data, + size_t count, loff_t *off) { struct fs3270 *fp; struct raw3270_request *rq; @@ -326,8 +318,7 @@ fs3270_write(struct file *filp, const char __user *data, size_t count, loff_t *o /* * process ioctl commands for the tube driver */ -static long -fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { char __user *argp; struct fs3270 *fp; @@ -374,8 +365,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /* * Allocate fs3270 structure. */ -static struct fs3270 * -fs3270_alloc_view(void) +static struct fs3270 *fs3270_alloc_view(void) { struct fs3270 *fp; @@ -393,8 +383,7 @@ fs3270_alloc_view(void) /* * Free fs3270 structure. */ -static void -fs3270_free_view(struct raw3270_view *view) +static void fs3270_free_view(struct raw3270_view *view) { struct fs3270 *fp; @@ -408,8 +397,7 @@ fs3270_free_view(struct raw3270_view *view) /* * Unlink fs3270 data structure from filp. */ -static void -fs3270_release(struct raw3270_view *view) +static void fs3270_release(struct raw3270_view *view) { struct fs3270 *fp; @@ -430,8 +418,7 @@ static struct raw3270_fn fs3270_fn = { /* * This routine is called whenever a 3270 fullscreen device is opened. */ -static int -fs3270_open(struct inode *inode, struct file *filp) +static int fs3270_open(struct inode *inode, struct file *filp) { struct fs3270 *fp; struct idal_buffer *ib; @@ -502,8 +489,7 @@ fs3270_open(struct inode *inode, struct file *filp) * This routine is called when the 3270 tty is closed. We wait * for the remaining request to be completed. Then we clean up. */ -static int -fs3270_close(struct inode *inode, struct file *filp) +static int fs3270_close(struct inode *inode, struct file *filp) { struct fs3270 *fp; @@ -551,8 +537,7 @@ static struct raw3270_notifier fs3270_notifier = { /* * 3270 fullscreen driver initialization. */ -static int __init -fs3270_init(void) +static int __init fs3270_init(void) { int rc; @@ -565,8 +550,7 @@ fs3270_init(void) return 0; } -static void __exit -fs3270_exit(void) +static void __exit fs3270_exit(void) { raw3270_unregister_notifier(&fs3270_notifier); device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, 0)); From a554dbd740bd45247a7b9617760f1c7e1b26ebfc Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 12:32:02 +0100 Subject: [PATCH 060/182] s390/fs3270: fix screen reset on activate fs3270 uses EWRITEA to clear the screen when a user opens /dev/3270/tub. However it misses the attribute byte after the EWRITEA, so (at least) x3270 complains about 'Record too short, missing write flags'. Add the missing flag byte to fix this. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/fs3270.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 7975dbea8d1b..aff38db1aa40 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -122,16 +122,19 @@ static int fs3270_activate(struct raw3270_view *view) if (!raw3270_request_final(fp->init)) return 0; + raw3270_request_set_cmd(fp->init, TC_EWRITEA); + raw3270_request_set_idal(fp->init, fp->rdbuf); + fp->init->rescnt = 0; + cp = fp->rdbuf->data[0]; if (fp->rdbuf_size == 0) { /* No saved buffer. Just clear the screen. */ - raw3270_request_set_cmd(fp->init, TC_EWRITEA); + fp->init->ccw.count = 1; fp->init->callback = fs3270_reset_callback; + cp[0] = 0; } else { /* Restore fullscreen buffer saved by fs3270_deactivate. */ - raw3270_request_set_cmd(fp->init, TC_EWRITEA); - raw3270_request_set_idal(fp->init, fp->rdbuf); fp->init->ccw.count = fp->rdbuf_size; - cp = fp->rdbuf->data[0]; + fp->init->callback = fs3270_restore_callback; cp[0] = TW_KR; cp[1] = TO_SBA; cp[2] = cp[6]; @@ -140,8 +143,6 @@ static int fs3270_activate(struct raw3270_view *view) cp[5] = TO_SBA; cp[6] = 0x40; cp[7] = 0x40; - fp->init->rescnt = 0; - fp->init->callback = fs3270_restore_callback; } rc = raw3270_start_locked(view, fp->init); fp->init->rc = rc; From 61f37f63f930a0d759fc3a98bb3a2330a34c6220 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 13:20:46 +0100 Subject: [PATCH 061/182] s390/fs3270: split header files In order to use the fs3270 one would need at least the ioctl definitions in uapi. Add two new include files in uapi, which contain: fs3270: ioctl number declarations + returned struct for TUBGETMOD. raw3270: all the orders, attributes and similar stuff used with 3270 terminals. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/fs3270.h | 25 ++++++++ arch/s390/include/uapi/asm/raw3270.h | 75 +++++++++++++++++++++++ drivers/s390/char/fs3270.c | 1 + drivers/s390/char/raw3270.h | 89 +--------------------------- 4 files changed, 102 insertions(+), 88 deletions(-) create mode 100644 arch/s390/include/uapi/asm/fs3270.h create mode 100644 arch/s390/include/uapi/asm/raw3270.h diff --git a/arch/s390/include/uapi/asm/fs3270.h b/arch/s390/include/uapi/asm/fs3270.h new file mode 100644 index 000000000000..c4bc1108af6a --- /dev/null +++ b/arch/s390/include/uapi/asm/fs3270.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_S390_UAPI_FS3270_H +#define __ASM_S390_UAPI_FS3270_H + +#include +#include + +/* ioctls for fullscreen 3270 */ +#define TUBICMD _IO('3', 3) /* set ccw command for fs reads. */ +#define TUBOCMD _IO('3', 4) /* set ccw command for fs writes. */ +#define TUBGETI _IO('3', 7) /* get ccw command for fs reads. */ +#define TUBGETO _IO('3', 8) /* get ccw command for fs writes. */ +#define TUBGETMOD _IO('3', 13) /* get characteristics like model, cols, rows */ + +/* For TUBGETMOD */ +struct raw3270_iocb { + __u16 model; + __u16 line_cnt; + __u16 col_cnt; + __u16 pf_cnt; + __u16 re_cnt; + __u16 map; +}; + +#endif /* __ASM_S390_UAPI_FS3270_H */ diff --git a/arch/s390/include/uapi/asm/raw3270.h b/arch/s390/include/uapi/asm/raw3270.h new file mode 100644 index 000000000000..6676f102bd50 --- /dev/null +++ b/arch/s390/include/uapi/asm/raw3270.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_S390_UAPI_RAW3270_H +#define __ASM_S390_UAPI_RAW3270_H + +/* Local Channel Commands */ +#define TC_WRITE 0x01 /* Write */ +#define TC_RDBUF 0x02 /* Read Buffer */ +#define TC_EWRITE 0x05 /* Erase write */ +#define TC_READMOD 0x06 /* Read modified */ +#define TC_EWRITEA 0x0d /* Erase write alternate */ +#define TC_WRITESF 0x11 /* Write structured field */ + +/* Buffer Control Orders */ +#define TO_GE 0x08 /* Graphics Escape */ +#define TO_SF 0x1d /* Start field */ +#define TO_SBA 0x11 /* Set buffer address */ +#define TO_IC 0x13 /* Insert cursor */ +#define TO_PT 0x05 /* Program tab */ +#define TO_RA 0x3c /* Repeat to address */ +#define TO_SFE 0x29 /* Start field extended */ +#define TO_EUA 0x12 /* Erase unprotected to address */ +#define TO_MF 0x2c /* Modify field */ +#define TO_SA 0x28 /* Set attribute */ + +/* Field Attribute Bytes */ +#define TF_INPUT 0x40 /* Visible input */ +#define TF_INPUTN 0x4c /* Invisible input */ +#define TF_INMDT 0xc1 /* Visible, Set-MDT */ +#define TF_LOG 0x60 + +/* Character Attribute Bytes */ +#define TAT_RESET 0x00 +#define TAT_FIELD 0xc0 +#define TAT_EXTHI 0x41 +#define TAT_FGCOLOR 0x42 +#define TAT_CHARS 0x43 +#define TAT_BGCOLOR 0x45 +#define TAT_TRANS 0x46 + +/* Extended-Highlighting Bytes */ +#define TAX_RESET 0x00 +#define TAX_BLINK 0xf1 +#define TAX_REVER 0xf2 +#define TAX_UNDER 0xf4 + +/* Reset value */ +#define TAR_RESET 0x00 + +/* Color values */ +#define TAC_RESET 0x00 +#define TAC_BLUE 0xf1 +#define TAC_RED 0xf2 +#define TAC_PINK 0xf3 +#define TAC_GREEN 0xf4 +#define TAC_TURQ 0xf5 +#define TAC_YELLOW 0xf6 +#define TAC_WHITE 0xf7 +#define TAC_DEFAULT 0x00 + +/* Write Control Characters */ +#define TW_NONE 0x40 /* No particular action */ +#define TW_KR 0xc2 /* Keyboard restore */ +#define TW_PLUSALARM 0x04 /* Add this bit for alarm */ + +#define RAW3270_FIRSTMINOR 1 /* First minor number */ +#define RAW3270_MAXDEVS 255 /* Max number of 3270 devices */ + +#define AID_CLEAR 0x6d +#define AID_ENTER 0x7d +#define AID_PF3 0xf3 +#define AID_PF7 0xf7 +#define AID_PF8 0xf8 +#define AID_READ_PARTITION 0x88 + +#endif /* __ASM_S390_UAPI_RAW3270_H */ diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index aff38db1aa40..4f26b0a55620 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index e8c71ef7bfa0..b1beecc7a0a9 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -8,97 +8,10 @@ * Copyright IBM Corp. 2003, 2009 */ +#include #include #include -/* ioctls for fullscreen 3270 */ -#define TUBICMD _IO('3', 3) /* set ccw command for fs reads. */ -#define TUBOCMD _IO('3', 4) /* set ccw command for fs writes. */ -#define TUBGETI _IO('3', 7) /* get ccw command for fs reads. */ -#define TUBGETO _IO('3', 8) /* get ccw command for fs writes. */ -#define TUBSETMOD _IO('3', 12) /* FIXME: what does it do ?*/ -#define TUBGETMOD _IO('3', 13) /* FIXME: what does it do ?*/ - -/* Local Channel Commands */ -#define TC_WRITE 0x01 /* Write */ -#define TC_RDBUF 0x02 /* Read Buffer */ -#define TC_EWRITE 0x05 /* Erase write */ -#define TC_READMOD 0x06 /* Read modified */ -#define TC_EWRITEA 0x0d /* Erase write alternate */ -#define TC_WRITESF 0x11 /* Write structured field */ - -/* Buffer Control Orders */ -#define TO_GE 0x08 /* Graphics Escape */ -#define TO_SF 0x1d /* Start field */ -#define TO_SBA 0x11 /* Set buffer address */ -#define TO_IC 0x13 /* Insert cursor */ -#define TO_PT 0x05 /* Program tab */ -#define TO_RA 0x3c /* Repeat to address */ -#define TO_SFE 0x29 /* Start field extended */ -#define TO_EUA 0x12 /* Erase unprotected to address */ -#define TO_MF 0x2c /* Modify field */ -#define TO_SA 0x28 /* Set attribute */ - -/* Field Attribute Bytes */ -#define TF_INPUT 0x40 /* Visible input */ -#define TF_INPUTN 0x4c /* Invisible input */ -#define TF_INMDT 0xc1 /* Visible, Set-MDT */ -#define TF_LOG 0x60 - -/* Character Attribute Bytes */ -#define TAT_RESET 0x00 -#define TAT_FIELD 0xc0 -#define TAT_EXTHI 0x41 -#define TAT_FGCOLOR 0x42 -#define TAT_CHARS 0x43 -#define TAT_BGCOLOR 0x45 -#define TAT_TRANS 0x46 - -/* Extended-Highlighting Bytes */ -#define TAX_RESET 0x00 -#define TAX_BLINK 0xf1 -#define TAX_REVER 0xf2 -#define TAX_UNDER 0xf4 - -/* Reset value */ -#define TAR_RESET 0x00 - -/* Color values */ -#define TAC_RESET 0x00 -#define TAC_BLUE 0xf1 -#define TAC_RED 0xf2 -#define TAC_PINK 0xf3 -#define TAC_GREEN 0xf4 -#define TAC_TURQ 0xf5 -#define TAC_YELLOW 0xf6 -#define TAC_WHITE 0xf7 -#define TAC_DEFAULT 0x00 - -/* Write Control Characters */ -#define TW_NONE 0x40 /* No particular action */ -#define TW_KR 0xc2 /* Keyboard restore */ -#define TW_PLUSALARM 0x04 /* Add this bit for alarm */ - -#define RAW3270_FIRSTMINOR 1 /* First minor number */ -#define RAW3270_MAXDEVS 255 /* Max number of 3270 devices */ - -#define AID_CLEAR 0x6d -#define AID_ENTER 0x7d -#define AID_PF3 0xf3 -#define AID_PF7 0xf7 -#define AID_PF8 0xf8 -#define AID_READ_PARTITION 0x88 - -/* For TUBGETMOD and TUBSETMOD. Should include. */ -struct raw3270_iocb { - short model; - short line_cnt; - short col_cnt; - short pf_cnt; - short re_cnt; - short map; -}; - struct raw3270; struct raw3270_view; extern struct class *class3270; From fe5e23dd983cc3e676f2f9355796e2505d889ce4 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 5 Dec 2022 15:20:46 +0100 Subject: [PATCH 062/182] s390/diag: use __packed __aligned Use __packed __aligned instead of __attribute__((packed, aligned(X))); to match the rest of the file. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/include/asm/diag.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/diag.h b/arch/s390/include/asm/diag.h index 9488b7ffb21c..674a939f16ee 100644 --- a/arch/s390/include/asm/diag.h +++ b/arch/s390/include/asm/diag.h @@ -81,7 +81,7 @@ struct diag210 { u8 vrdccrty; /* real device type (output) */ u8 vrdccrmd; /* real device model (output) */ u8 vrdccrft; /* real device feature (output) */ -} __attribute__((packed, aligned(4))); +} __packed __aligned(4); extern int diag210(struct diag210 *addr); From 9975fde09e50b9ac9bab49cafac4ebc32cf4044c Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 7 Dec 2022 22:18:38 +0100 Subject: [PATCH 063/182] s390/con3270: return from notifier when activate view fails When activating the view fails (in this case because the 3270 is disconnected) return from the notifer callback. Otherwise the system will deadlock. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 7d163516ee45..5fa1f080d5f3 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -2079,12 +2079,16 @@ static int con3270_notify(struct notifier_block *self, { struct tty3270 *tp; unsigned long flags; + int rc; tp = condev; if (!tp->view.dev) return NOTIFY_DONE; - if (!raw3270_view_lock_unavailable(&tp->view)) - raw3270_activate_view(&tp->view); + if (!raw3270_view_lock_unavailable(&tp->view)) { + rc = raw3270_activate_view(&tp->view); + if (rc) + return NOTIFY_DONE; + } if (!spin_trylock_irqsave(&tp->view.lock, flags)) return NOTIFY_DONE; con3270_wait_write(tp); From da4e272e831c6b1ab5169e5f7a51b21b7e364c22 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 8 Dec 2022 08:41:17 +0100 Subject: [PATCH 064/182] s390/con3270: simplify update flags Make TTY3270_UPDATE_ALL the sum of all TTY3270_* flags, so we don't need any special handling for it. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 5fa1f080d5f3..6d21e16a92cb 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -127,10 +127,9 @@ struct tty3270 { }; /* tty3270->update_flags. See tty3270_update for details. */ -#define TTY_UPDATE_ERASE 1 /* Use EWRITEA instead of WRITE. */ -#define TTY_UPDATE_INPUT 4 /* Update input line. */ -#define TTY_UPDATE_STATUS 8 /* Update status line. */ -#define TTY_UPDATE_ALL 16 /* Recreate screen. */ +#define TTY_UPDATE_INPUT 0x1 /* Update input line. */ +#define TTY_UPDATE_STATUS 0x2 /* Update status line. */ +#define TTY_UPDATE_ALL 0x3 /* Recreate screen. */ #define TTY3270_INPUT_AREA_ROWS 2 @@ -491,7 +490,7 @@ static void tty3270_update(struct timer_list *t) struct tty3270 *tp = from_timer(tp, t, timer); struct raw3270_request *wrq; struct tty3270_line *line; - unsigned long updated; + u8 cmd = TC_WRITE; int i, rc, len; wrq = xchg(&tp->write, 0); @@ -501,18 +500,10 @@ static void tty3270_update(struct timer_list *t) } spin_lock_irq(&tp->view.lock); - updated = 0; - if (tp->update_flags & TTY_UPDATE_ALL) { - tp->update_flags = TTY_UPDATE_ERASE | - TTY_UPDATE_INPUT | TTY_UPDATE_STATUS; - } - if (tp->update_flags & TTY_UPDATE_ERASE) { - /* Use erase write alternate to erase display. */ - raw3270_request_set_cmd(wrq, TC_EWRITEA); - updated |= TTY_UPDATE_ERASE; - } else { - raw3270_request_set_cmd(wrq, TC_WRITE); - } + if (tp->update_flags == TTY_UPDATE_ALL) + cmd = TC_EWRITEA; + + raw3270_request_set_cmd(wrq, cmd); raw3270_request_add_data(wrq, &tp->wcc, 1); tp->wcc = TW_NONE; @@ -522,7 +513,7 @@ static void tty3270_update(struct timer_list *t) if (tp->update_flags & TTY_UPDATE_STATUS) { len = tty3270_add_status(tp); if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) - updated |= TTY_UPDATE_STATUS; + tp->update_flags &= ~TTY_UPDATE_STATUS; } /* @@ -531,7 +522,7 @@ static void tty3270_update(struct timer_list *t) if (tp->update_flags & TTY_UPDATE_INPUT) { len = tty3270_add_prompt(tp); if (raw3270_request_add_data(wrq, tp->converted_line, len) == 0) - updated |= TTY_UPDATE_INPUT; + tp->update_flags &= ~TTY_UPDATE_INPUT; } for (i = 0; i < tty3270_tty_rows(tp); i++) { @@ -551,7 +542,6 @@ static void tty3270_update(struct timer_list *t) wrq->callback = tty3270_write_callback; rc = raw3270_start(&tp->view, wrq); if (rc == 0) { - tp->update_flags &= ~updated; if (tp->update_flags) tty3270_set_timer(tp, 1); } else { From 422a78ea359a8b637556f106ffaf7612e20ed122 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 8 Dec 2022 09:24:06 +0100 Subject: [PATCH 065/182] s390/con3270: set SBA and RA addresses when converting lines Now that lines are converted during output, the RA and SBA no longer need to get updated as an additional step. Instead set them when converting the line. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 6d21e16a92cb..2d867f6f0b76 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -281,20 +281,6 @@ static int tty3270_add_status(struct tty3270 *tp) return cp - (char *)tp->converted_line; } -/* - * Set output offsets to 3270 datastream fragment of a tty string. - * (TO_SBA offset at the start and TO_RA offset at the end of the string) - */ -static void tty3270_update_string(struct tty3270 *tp, char *line, int len, int nr) -{ - unsigned char *cp; - - raw3270_buffer_address(tp->view.dev, line + 1, 0, nr); - cp = line + len - 4; - if (*cp == TO_RA) - raw3270_buffer_address(tp->view.dev, cp + 1, 0, nr + 1); -} - static void tty3270_blank_screen(struct tty3270 *tp) { struct tty3270_line *line; @@ -364,7 +350,7 @@ static int tty3270_required_length(struct tty3270 *tp, struct tty3270_line *line } static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_line *line, - char *cp, struct tty3270_attribute *attr) + char *cp, struct tty3270_attribute *attr, int lineno) { if (attr->highlight) cp = tty3270_add_sa(tp, cp, TAT_EXTHI, TAX_RESET); @@ -373,7 +359,7 @@ static char *tty3270_add_reset_attributes(struct tty3270 *tp, struct tty3270_lin if (attr->b_color != TAC_RESET) cp = tty3270_add_sa(tp, cp, TAT_BGCOLOR, TAX_RESET); if (line->len < tp->view.cols) - cp = tty3270_add_ra(tp, cp, 0, 0, 0); + cp = tty3270_add_ra(tp, cp, 0, lineno + 1, 0); return cp; } @@ -408,7 +394,7 @@ static char tty3270_graphics_translate(struct tty3270 *tp, char ch) } static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *line, - struct tty3270_attribute *attr, char *cp) + struct tty3270_attribute *attr, char *cp, int lineno) { const unsigned char colors[16] = { [0] = TAC_DEFAULT, @@ -431,7 +417,7 @@ static char *tty3270_add_attributes(struct tty3270 *tp, struct tty3270_line *lin struct tty3270_cell *cell; int c, i; - cp = tty3270_add_ba(tp, cp, TO_SBA, 0, 0); + cp = tty3270_add_ba(tp, cp, TO_SBA, 0, lineno); for (i = 0, cell = line->cells; i < line->len; i++, cell++) { if (cell->attributes.highlight != attr->highlight) { @@ -465,7 +451,7 @@ static void tty3270_reset_attributes(struct tty3270_attribute *attr) /* * Convert a tty3270_line to a 3270 data fragment usable for output. */ -static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line) +static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line *line, int lineno) { struct tty3270_attribute attr; int flen; @@ -477,8 +463,8 @@ static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line return 0; /* Write 3270 data fragment. */ tty3270_reset_attributes(&attr); - cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line); - cp = tty3270_add_reset_attributes(tp, line, cp, &attr); + cp = tty3270_add_attributes(tp, line, &attr, tp->converted_line, lineno); + cp = tty3270_add_reset_attributes(tp, line, cp, &attr, lineno); return cp - (char *)tp->converted_line; } @@ -529,8 +515,7 @@ static void tty3270_update(struct timer_list *t) line = tty3270_get_view_line(tp, i); if (!line->dirty) continue; - len = tty3270_convert_line(tp, line); - tty3270_update_string(tp, tp->converted_line, len, i); + len = tty3270_convert_line(tp, line, i); if (raw3270_request_add_data(wrq, tp->converted_line, len)) break; line->dirty = 0; From ba5c2e2ae4806b4e5810153d9d9581593b65773b Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 8 Dec 2022 20:58:26 +0100 Subject: [PATCH 066/182] s390/con3270: add special output handling when oops_in_progress is set Normally a user can scroll back with PF7/PF8 if printed messages are outside of the visible screen area. This doesn't work when the kernel crashes, because the scrollback handling is done by the kernel, which is no longer alive after the kernel crash. Add code to always print all dirty lines in the screen buffer, so the user can scroll back with the terminal scrollback keys (Page Up/Down). Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 76 +++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 2d867f6f0b76..9402690de598 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -91,6 +91,7 @@ struct tty3270 { char *converted_line; /* RAW 3270 data stream */ unsigned int line_view_start; /* Start of visible area */ unsigned int line_write_start; /* current write position */ + unsigned int oops_line; /* line counter used when print oops */ /* Current tty screen. */ unsigned int cx, cy; /* Current output position. */ @@ -129,7 +130,8 @@ struct tty3270 { /* tty3270->update_flags. See tty3270_update for details. */ #define TTY_UPDATE_INPUT 0x1 /* Update input line. */ #define TTY_UPDATE_STATUS 0x2 /* Update status line. */ -#define TTY_UPDATE_ALL 0x3 /* Recreate screen. */ +#define TTY_UPDATE_LINES 0x4 /* Update visible screen lines */ +#define TTY_UPDATE_ALL 0x7 /* Recreate screen. */ #define TTY3270_INPUT_AREA_ROWS 2 @@ -274,7 +276,7 @@ static int tty3270_add_status(struct tty3270 *tp) codepage_convert(tp->view.ascebc, cp, len); cp += len; } else { - cp = tty3270_ebcdic_convert(tp, cp, "Running"); + cp = tty3270_ebcdic_convert(tp, cp, oops_in_progress ? "Crashed" : "Running"); } cp = tty3270_add_sf(tp, cp, TF_LOG); cp = tty3270_add_sa(tp, cp, TAT_FGCOLOR, TAC_RESET); @@ -468,6 +470,55 @@ static unsigned int tty3270_convert_line(struct tty3270 *tp, struct tty3270_line return cp - (char *)tp->converted_line; } +static void tty3270_update_lines_visible(struct tty3270 *tp, struct raw3270_request *rq) +{ + struct tty3270_line *line; + int len, i; + + for (i = 0; i < tty3270_tty_rows(tp); i++) { + line = tty3270_get_view_line(tp, i); + if (!line->dirty) + continue; + len = tty3270_convert_line(tp, line, i); + if (raw3270_request_add_data(rq, tp->converted_line, len)) + break; + line->dirty = 0; + } + if (i == tty3270_tty_rows(tp)) { + for (i = 0; i < tp->allocated_lines; i++) + tp->screen[i].dirty = 0; + tp->update_flags &= ~TTY_UPDATE_LINES; + } +} + +static void tty3270_update_lines_all(struct tty3270 *tp, struct raw3270_request *rq) +{ + struct tty3270_line *line; + char buf[4]; + int len, i; + + for (i = 0; i < tp->allocated_lines; i++) { + line = tty3270_get_write_line(tp, i + tp->cy + 1); + if (!line->dirty) + continue; + len = tty3270_convert_line(tp, line, tp->oops_line); + if (raw3270_request_add_data(rq, tp->converted_line, len)) + break; + line->dirty = 0; + if (++tp->oops_line >= tty3270_tty_rows(tp)) + tp->oops_line = 0; + } + + if (i == tp->allocated_lines) { + if (tp->oops_line < tty3270_tty_rows(tp)) { + tty3270_add_ra(tp, buf, 0, tty3270_tty_rows(tp), 0); + if (raw3270_request_add_data(rq, buf, sizeof(buf))) + return; + } + tp->update_flags &= ~TTY_UPDATE_LINES; + } +} + /* * Update 3270 display. */ @@ -475,9 +526,8 @@ static void tty3270_update(struct timer_list *t) { struct tty3270 *tp = from_timer(tp, t, timer); struct raw3270_request *wrq; - struct tty3270_line *line; u8 cmd = TC_WRITE; - int i, rc, len; + int rc, len; wrq = xchg(&tp->write, 0); if (!wrq) { @@ -511,19 +561,13 @@ static void tty3270_update(struct timer_list *t) tp->update_flags &= ~TTY_UPDATE_INPUT; } - for (i = 0; i < tty3270_tty_rows(tp); i++) { - line = tty3270_get_view_line(tp, i); - if (!line->dirty) - continue; - len = tty3270_convert_line(tp, line, i); - if (raw3270_request_add_data(wrq, tp->converted_line, len)) - break; - line->dirty = 0; + if (tp->update_flags & TTY_UPDATE_LINES) { + if (oops_in_progress) + tty3270_update_lines_all(tp, wrq); + else + tty3270_update_lines_visible(tp, wrq); } - if (i < tty3270_tty_rows(tp) - 1) - tty3270_set_timer(tp, 1); - wrq->callback = tty3270_write_callback; rc = raw3270_start(&tp->view, wrq); if (rc == 0) { @@ -1750,6 +1794,7 @@ static void tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, } } /* Setup timer to update display after 1/10 second */ + tp->update_flags |= TTY_UPDATE_LINES; if (!timer_pending(&tp->timer)) tty3270_set_timer(tp, msecs_to_jiffies(100)); @@ -2068,6 +2113,7 @@ static int con3270_notify(struct notifier_block *self, return NOTIFY_DONE; con3270_wait_write(tp); tp->nr_up = 0; + tp->update_flags = TTY_UPDATE_ALL; while (tp->update_flags != 0) { spin_unlock_irqrestore(&tp->view.lock, flags); tty3270_update(&tp->timer); From 8a54e238ef1eb6edd50335ee8626f6962c3dfb4f Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Mon, 25 Jul 2022 22:21:14 +0200 Subject: [PATCH 067/182] vfio/ccw: cleanup some of the mdev commentary There is no longer an mdev struct accessible via a channel program struct, but there are some artifacts remaining that mention it. Clean them up. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 5 ++--- drivers/s390/cio/vfio_ccw_cp.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index c0a09fa8991a..9e6df1f2fbee 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -121,7 +121,7 @@ static void page_array_unpin(struct page_array *pa, /* * page_array_pin() - Pin user pages in memory * @pa: page_array on which to perform the operation - * @mdev: the mediated device to perform pin operations + * @vdev: the vfio device to perform pin operations * * Returns number of pages pinned upon success. * If the pin request partially succeeds, or fails completely, @@ -229,7 +229,7 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) } /* - * Within the domain (@mdev), copy @n bytes from a guest physical + * Within the domain (@vdev), copy @n bytes from a guest physical * address (@iova) to a host physical address (@to). */ static long copy_from_iova(struct vfio_device *vdev, void *to, u64 iova, @@ -665,7 +665,6 @@ static int ccwchain_fetch_one(struct ccwchain *chain, /** * cp_init() - allocate ccwchains for a channel program. * @cp: channel_program on which to perform the operation - * @mdev: the mediated device to perform pin/unpin operations * @orb: control block for the channel program from the guest * * This creates one or more ccwchain(s), and copies the raw data of diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index 54d26e242533..16138a654fdd 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -27,7 +27,6 @@ * struct channel_program - manage information for channel program * @ccwchain_list: list head of ccwchains * @orb: orb for the currently processed ssch request - * @mdev: the mediated device to perform page pinning/unpinning * @initialized: whether this instance is actually initialized * * @ccwchain_list is the head of a ccwchain list, that contents the From 9fbed59fcd16e60dde2528038cc343abd65c0948 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Thu, 10 Nov 2022 03:30:23 +0100 Subject: [PATCH 068/182] vfio/ccw: simplify the cp_get_orb interface There's no need to send in both the address of the subchannel struct, and an element within it, to populate the ORB. Pass the whole pointer and let cp_get_orb() take the pieces that are needed. Suggested-by: Matthew Rosato Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 9 ++++----- drivers/s390/cio/vfio_ccw_cp.h | 2 +- drivers/s390/cio/vfio_ccw_fsm.c | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 9e6df1f2fbee..a0060ef1119e 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -816,14 +816,13 @@ int cp_prefetch(struct channel_program *cp) /** * cp_get_orb() - get the orb of the channel program * @cp: channel_program on which to perform the operation - * @intparm: new intparm for the returned orb - * @lpm: candidate value of the logical-path mask for the returned orb + * @sch: subchannel the operation will be performed against * * This function returns the address of the updated orb of the channel * program. Channel I/O device drivers could use this orb to issue a * ssch. */ -union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm) +union orb *cp_get_orb(struct channel_program *cp, struct subchannel *sch) { union orb *orb; struct ccwchain *chain; @@ -835,12 +834,12 @@ union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm) orb = &cp->orb; - orb->cmd.intparm = intparm; + orb->cmd.intparm = (u32)virt_to_phys(sch); orb->cmd.fmt = 1; orb->cmd.key = PAGE_DEFAULT_KEY >> 4; if (orb->cmd.lpm == 0) - orb->cmd.lpm = lpm; + orb->cmd.lpm = sch->lpm; chain = list_first_entry(&cp->ccwchain_list, struct ccwchain, next); cpa = chain->ch_ccw; diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index 16138a654fdd..fc31eb699807 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -43,7 +43,7 @@ struct channel_program { int cp_init(struct channel_program *cp, union orb *orb); void cp_free(struct channel_program *cp); int cp_prefetch(struct channel_program *cp); -union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm); +union orb *cp_get_orb(struct channel_program *cp, struct subchannel *sch); void cp_update_scsw(struct channel_program *cp, union scsw *scsw); bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length); diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 2784a4e4d2be..757b73141246 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -27,7 +27,7 @@ static int fsm_io_helper(struct vfio_ccw_private *private) spin_lock_irqsave(sch->lock, flags); - orb = cp_get_orb(&private->cp, (u32)virt_to_phys(sch), sch->lpm); + orb = cp_get_orb(&private->cp, sch); if (!orb) { ret = -EIO; goto out; From 155a4321c117e29d174893127ae84cd84cacf0f3 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Thu, 18 Apr 2019 20:26:15 +0200 Subject: [PATCH 069/182] vfio/ccw: allow non-zero storage keys Currently, vfio-ccw copies the ORB from the io_region to the channel_program struct being built. It then adjusts various pieces of that ORB to the values needed to be used by the SSCH issued by vfio-ccw in the host. This includes setting the subchannel key to the default, presumably because Linux doesn't do anything with non-zero storage keys itself. But it seems wrong to convert every I/O to the default key if the guest itself requested a non-zero subchannel (access) key. Any channel program that sets a non-zero key would expect the same key returned in the SCSW of the IRB, not zero, so best to allow that to occur unimpeded. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index a0060ef1119e..268a90252521 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -836,7 +836,6 @@ union orb *cp_get_orb(struct channel_program *cp, struct subchannel *sch) orb->cmd.intparm = (u32)virt_to_phys(sch); orb->cmd.fmt = 1; - orb->cmd.key = PAGE_DEFAULT_KEY >> 4; if (orb->cmd.lpm == 0) orb->cmd.lpm = sch->lpm; From 254cb663c2ace586191f9b0676277b89450a76e7 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Wed, 2 Dec 2020 19:19:30 +0100 Subject: [PATCH 070/182] vfio/ccw: move where IDA flag is set in ORB The output of vfio_ccw is always a Format-2 IDAL, but the code that explicitly sets this is buried in cp_init(). In fact the input is often already a Format-2 IDAL, and would be rejected (via the check in ccwchain_calc_length()) if it weren't, so explicitly setting it doesn't do much. Setting it way down here only makes it impossible to make decisions in support of other IDAL formats. Let's move that to where the rest of the ORB is set up, so that the CCW processing in cp_prefetch() is performed according to the contents of the unmodified guest ORB. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 268a90252521..3a11132b1685 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -707,15 +707,9 @@ int cp_init(struct channel_program *cp, union orb *orb) /* Build a ccwchain for the first CCW segment */ ret = ccwchain_handle_ccw(orb->cmd.cpa, cp); - if (!ret) { + if (!ret) cp->initialized = true; - /* It is safe to force: if it was not set but idals used - * ccwchain_calc_length would have returned an error. - */ - cp->orb.cmd.c64 = 1; - } - return ret; } @@ -837,6 +831,11 @@ union orb *cp_get_orb(struct channel_program *cp, struct subchannel *sch) orb->cmd.intparm = (u32)virt_to_phys(sch); orb->cmd.fmt = 1; + /* + * Everything built by vfio-ccw is a Format-2 IDAL. + */ + orb->cmd.c64 = 1; + if (orb->cmd.lpm == 0) orb->cmd.lpm = sch->lpm; From c5e8083f9580bd7b32ca3967e3d2f99b38cfdaa6 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Thu, 7 Jul 2022 17:30:26 +0200 Subject: [PATCH 071/182] vfio/ccw: replace copy_from_iova with vfio_dma_rw It was suggested [1] that we replace the old copy_from_iova() routine (which pins a page, does a memcpy, and unpins the page) with the newer vfio_dma_rw() interface. This has a modest improvement in the overall time spent through the fsm_io_request() path, and simplifies some of the code to boot. [1] https://lore.kernel.org/r/20220706170553.GK693670@nvidia.com/ Suggested-by: Jason Gunthorpe Signed-off-by: Eric Farman Reviewed-by: Jason Gunthorpe Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 56 +++------------------------------- 1 file changed, 5 insertions(+), 51 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 3a11132b1685..1eacbb8dc860 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -228,51 +228,6 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) } } -/* - * Within the domain (@vdev), copy @n bytes from a guest physical - * address (@iova) to a host physical address (@to). - */ -static long copy_from_iova(struct vfio_device *vdev, void *to, u64 iova, - unsigned long n) -{ - struct page_array pa = {0}; - int i, ret; - unsigned long l, m; - - ret = page_array_alloc(&pa, iova, n); - if (ret < 0) - return ret; - - ret = page_array_pin(&pa, vdev); - if (ret < 0) { - page_array_unpin_free(&pa, vdev); - return ret; - } - - l = n; - for (i = 0; i < pa.pa_nr; i++) { - void *from = kmap_local_page(pa.pa_page[i]); - - m = PAGE_SIZE; - if (i == 0) { - from += iova & (PAGE_SIZE - 1); - m -= iova & (PAGE_SIZE - 1); - } - - m = min(l, m); - memcpy(to + (n - l), from, m); - kunmap_local(from); - - l -= m; - if (l == 0) - break; - } - - page_array_unpin_free(&pa, vdev); - - return l; -} - /* * Helpers to operate ccwchain. */ @@ -471,10 +426,9 @@ static int ccwchain_handle_ccw(u32 cda, struct channel_program *cp) int len, ret; /* Copy 2K (the most we support today) of possible CCWs */ - len = copy_from_iova(vdev, cp->guest_cp, cda, - CCWCHAIN_LEN_MAX * sizeof(struct ccw1)); - if (len) - return len; + ret = vfio_dma_rw(vdev, cda, cp->guest_cp, CCWCHAIN_LEN_MAX * sizeof(struct ccw1), false); + if (ret) + return ret; /* Convert any Format-0 CCWs to Format-1 */ if (!cp->orb.cmd.fmt) @@ -572,7 +526,7 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, if (ccw_is_idal(ccw)) { /* Read first IDAW to see if it's 4K-aligned or not. */ /* All subsequent IDAws will be 4K-aligned. */ - ret = copy_from_iova(vdev, &iova, ccw->cda, sizeof(iova)); + ret = vfio_dma_rw(vdev, ccw->cda, &iova, sizeof(iova), false); if (ret) return ret; } else { @@ -601,7 +555,7 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, if (ccw_is_idal(ccw)) { /* Copy guest IDAL into host IDAL */ - ret = copy_from_iova(vdev, idaws, ccw->cda, idal_len); + ret = vfio_dma_rw(vdev, ccw->cda, idaws, idal_len, false); if (ret) goto out_unpin; From a4c6040472ba638f2719f371fad92c83365f7332 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 21 Oct 2022 15:32:30 +0200 Subject: [PATCH 072/182] vfio/ccw: simplify CCW chain fetch routines The act of processing a fetched CCW has two components: 1) Process a Transfer-in-channel (TIC) CCW 2) Process any other CCW The former needs to look at whether the TIC jumps backwards into the current channel program or forwards into a new segment, while the latter just processes the CCW data address itself. Rather than passing the chain segment and index within it to the handlers for the above, and requiring each to calculate the elements it needs, simply pass the needed pointers directly. For the TIC, that means the CCW being processed and the location of the entire channel program which holds all segments. For the other CCWs, the page_array pointer is also needed to perform the page pinning, etc. While at it, rename ccwchain_fetch_direct to _ccw, to indicate what it is. The name "_direct" is historical, when it used to process a direct-addressed CCW, but IDAs are processed here too. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 1eacbb8dc860..d41d94cecdf8 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -482,11 +482,9 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp) return 0; } -static int ccwchain_fetch_tic(struct ccwchain *chain, - int idx, +static int ccwchain_fetch_tic(struct ccw1 *ccw, struct channel_program *cp) { - struct ccw1 *ccw = chain->ch_ccw + idx; struct ccwchain *iter; u32 ccw_head; @@ -502,14 +500,12 @@ static int ccwchain_fetch_tic(struct ccwchain *chain, return -EFAULT; } -static int ccwchain_fetch_direct(struct ccwchain *chain, - int idx, - struct channel_program *cp) +static int ccwchain_fetch_ccw(struct ccw1 *ccw, + struct page_array *pa, + struct channel_program *cp) { struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; - struct ccw1 *ccw; - struct page_array *pa; u64 iova; unsigned long *idaws; int ret; @@ -517,8 +513,6 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, int idaw_nr, idal_len; int i; - ccw = chain->ch_ccw + idx; - if (ccw->count) bytes = ccw->count; @@ -548,7 +542,6 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, * required for the data transfer, since we only only support * 4K IDAWs today. */ - pa = chain->ch_pa + idx; ret = page_array_alloc(pa, iova, bytes); if (ret < 0) goto out_free_idaws; @@ -604,16 +597,15 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, * and to get rid of the cda 2G limitiaion of ccw1, we'll translate * direct ccws to idal ccws. */ -static int ccwchain_fetch_one(struct ccwchain *chain, - int idx, +static int ccwchain_fetch_one(struct ccw1 *ccw, + struct page_array *pa, struct channel_program *cp) + { - struct ccw1 *ccw = chain->ch_ccw + idx; - if (ccw_is_tic(ccw)) - return ccwchain_fetch_tic(chain, idx, cp); + return ccwchain_fetch_tic(ccw, cp); - return ccwchain_fetch_direct(chain, idx, cp); + return ccwchain_fetch_ccw(ccw, pa, cp); } /** @@ -736,6 +728,8 @@ void cp_free(struct channel_program *cp) int cp_prefetch(struct channel_program *cp) { struct ccwchain *chain; + struct ccw1 *ccw; + struct page_array *pa; int len, idx, ret; /* this is an error in the caller */ @@ -745,7 +739,10 @@ int cp_prefetch(struct channel_program *cp) list_for_each_entry(chain, &cp->ccwchain_list, next) { len = chain->ch_len; for (idx = 0; idx < len; idx++) { - ret = ccwchain_fetch_one(chain, idx, cp); + ccw = chain->ch_ccw + idx; + pa = chain->ch_pa + idx; + + ret = ccwchain_fetch_one(ccw, pa, cp); if (ret) goto out_err; } From 4b946d65b8aa0071dbbc54b35b8502fa99c1ee22 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Thu, 22 Oct 2020 16:54:32 +0200 Subject: [PATCH 073/182] vfio/ccw: remove unnecessary malloc alignment Everything about this allocation is harder than necessary, since the memory allocation is already aligned to our needs. Break them apart for readability, instead of doing the funky arithmetic. Of the structures that are involved, only ch_ccw needs the GFP_DMA flag, so the others can be allocated without it. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 41 ++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index d41d94cecdf8..99332c6f6010 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -311,40 +311,41 @@ static inline int is_tic_within_range(struct ccw1 *ccw, u32 head, int len) static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len) { struct ccwchain *chain; - void *data; - size_t size; - /* Make ccw address aligned to 8. */ - size = ((sizeof(*chain) + 7L) & -8L) + - sizeof(*chain->ch_ccw) * len + - sizeof(*chain->ch_pa) * len; - chain = kzalloc(size, GFP_DMA | GFP_KERNEL); + chain = kzalloc(sizeof(*chain), GFP_KERNEL); if (!chain) return NULL; - data = (u8 *)chain + ((sizeof(*chain) + 7L) & -8L); - chain->ch_ccw = (struct ccw1 *)data; + chain->ch_ccw = kcalloc(len, sizeof(*chain->ch_ccw), GFP_DMA | GFP_KERNEL); + if (!chain->ch_ccw) + goto out_err; - data = (u8 *)(chain->ch_ccw) + sizeof(*chain->ch_ccw) * len; - chain->ch_pa = (struct page_array *)data; - - chain->ch_len = len; + chain->ch_pa = kcalloc(len, sizeof(*chain->ch_pa), GFP_KERNEL); + if (!chain->ch_pa) + goto out_err; list_add_tail(&chain->next, &cp->ccwchain_list); return chain; + +out_err: + kfree(chain->ch_ccw); + kfree(chain); + return NULL; } static void ccwchain_free(struct ccwchain *chain) { list_del(&chain->next); + kfree(chain->ch_pa); + kfree(chain->ch_ccw); kfree(chain); } /* Free resource for a ccw that allocated memory for its cda. */ static void ccwchain_cda_free(struct ccwchain *chain, int idx) { - struct ccw1 *ccw = chain->ch_ccw + idx; + struct ccw1 *ccw = &chain->ch_ccw[idx]; if (ccw_is_tic(ccw)) return; @@ -443,6 +444,8 @@ static int ccwchain_handle_ccw(u32 cda, struct channel_program *cp) chain = ccwchain_alloc(cp, len); if (!chain) return -ENOMEM; + + chain->ch_len = len; chain->ch_iova = cda; /* Copy the actual CCWs into the new chain */ @@ -464,7 +467,7 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp) int i, ret; for (i = 0; i < chain->ch_len; i++) { - tic = chain->ch_ccw + i; + tic = &chain->ch_ccw[i]; if (!ccw_is_tic(tic)) continue; @@ -681,7 +684,7 @@ void cp_free(struct channel_program *cp) cp->initialized = false; list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) { - page_array_unpin_free(chain->ch_pa + i, vdev); + page_array_unpin_free(&chain->ch_pa[i], vdev); ccwchain_cda_free(chain, i); } ccwchain_free(chain); @@ -739,8 +742,8 @@ int cp_prefetch(struct channel_program *cp) list_for_each_entry(chain, &cp->ccwchain_list, next) { len = chain->ch_len; for (idx = 0; idx < len; idx++) { - ccw = chain->ch_ccw + idx; - pa = chain->ch_pa + idx; + ccw = &chain->ch_ccw[idx]; + pa = &chain->ch_pa[idx]; ret = ccwchain_fetch_one(ccw, pa, cp); if (ret) @@ -866,7 +869,7 @@ bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length) list_for_each_entry(chain, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) - if (page_array_iova_pinned(chain->ch_pa + i, iova, length)) + if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) return true; } From 62a97a56a64c97c3865e55d702babc22f3b2ea6a Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 21 Oct 2022 16:59:13 +0200 Subject: [PATCH 074/182] vfio/ccw: pass page count to page_array struct The allocation of our page_array struct calculates the number of 4K pages that would be needed to hold a certain number of bytes. But, since the number of pages that will be pinned is also calculated by the length of the IDAL, this logic is unnecessary. Let's pass that information in directly, and avoid the math within the allocator. Also, let's make this two allocations instead of one, to make it apparent what's happening within here. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 99332c6f6010..b1436736b7b6 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -43,7 +43,7 @@ struct ccwchain { * page_array_alloc() - alloc memory for page array * @pa: page_array on which to perform the operation * @iova: target guest physical address - * @len: number of bytes that should be pinned from @iova + * @len: number of pages that should be pinned from @iova * * Attempt to allocate memory for page array. * @@ -63,18 +63,20 @@ static int page_array_alloc(struct page_array *pa, u64 iova, unsigned int len) if (pa->pa_nr || pa->pa_iova) return -EINVAL; - pa->pa_nr = ((iova & ~PAGE_MASK) + len + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - if (!pa->pa_nr) + if (len == 0) return -EINVAL; - pa->pa_iova = kcalloc(pa->pa_nr, - sizeof(*pa->pa_iova) + sizeof(*pa->pa_page), - GFP_KERNEL); - if (unlikely(!pa->pa_iova)) { - pa->pa_nr = 0; + pa->pa_nr = len; + + pa->pa_iova = kcalloc(len, sizeof(*pa->pa_iova), GFP_KERNEL); + if (!pa->pa_iova) + return -ENOMEM; + + pa->pa_page = kcalloc(len, sizeof(*pa->pa_page), GFP_KERNEL); + if (!pa->pa_page) { + kfree(pa->pa_iova); return -ENOMEM; } - pa->pa_page = (struct page **)&pa->pa_iova[pa->pa_nr]; pa->pa_iova[0] = iova; pa->pa_page[0] = NULL; @@ -167,6 +169,7 @@ static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vdev) { page_array_unpin(pa, vdev, pa->pa_nr); + kfree(pa->pa_page); kfree(pa->pa_iova); } @@ -545,7 +548,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, * required for the data transfer, since we only only support * 4K IDAWs today. */ - ret = page_array_alloc(pa, iova, bytes); + ret = page_array_alloc(pa, iova, idaw_nr); if (ret < 0) goto out_free_idaws; From 61783394f4eb3a8a0944005ea2761c011788a9c3 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 21 Oct 2022 17:02:48 +0200 Subject: [PATCH 075/182] vfio/ccw: populate page_array struct inline There are two possible ways the list of addresses that get passed to vfio are calculated. One is from a guest IDAL, which would be an array of (probably) non-contiguous addresses. The other is built from contiguous pages that follow the starting address provided by ccw->cda. page_array_alloc() attempts to simplify things by pre-populating this array from the starting address, but that's not needed for a CCW with an IDAL anyway so doesn't need to be in the allocator. Move it to the caller in the non-IDAL case, since it will be overwritten when reading the guest IDAL. Remove the initialization of the pa_page output pointers, since it won't be explicitly needed for either case. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index b1436736b7b6..f448aa93007f 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -42,7 +42,6 @@ struct ccwchain { /* * page_array_alloc() - alloc memory for page array * @pa: page_array on which to perform the operation - * @iova: target guest physical address * @len: number of pages that should be pinned from @iova * * Attempt to allocate memory for page array. @@ -56,10 +55,8 @@ struct ccwchain { * -EINVAL if pa->pa_nr is not initially zero, or pa->pa_iova is not NULL * -ENOMEM if alloc failed */ -static int page_array_alloc(struct page_array *pa, u64 iova, unsigned int len) +static int page_array_alloc(struct page_array *pa, unsigned int len) { - int i; - if (pa->pa_nr || pa->pa_iova) return -EINVAL; @@ -78,13 +75,6 @@ static int page_array_alloc(struct page_array *pa, u64 iova, unsigned int len) return -ENOMEM; } - pa->pa_iova[0] = iova; - pa->pa_page[0] = NULL; - for (i = 1; i < pa->pa_nr; i++) { - pa->pa_iova[i] = pa->pa_iova[i - 1] + PAGE_SIZE; - pa->pa_page[i] = NULL; - } - return 0; } @@ -548,7 +538,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, * required for the data transfer, since we only only support * 4K IDAWs today. */ - ret = page_array_alloc(pa, iova, idaw_nr); + ret = page_array_alloc(pa, idaw_nr); if (ret < 0) goto out_free_idaws; @@ -565,11 +555,9 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, for (i = 0; i < idaw_nr; i++) pa->pa_iova[i] = idaws[i]; } else { - /* - * No action is required here; the iova addresses in page_array - * were initialized sequentially in page_array_alloc() beginning - * with the contents of ccw->cda. - */ + pa->pa_iova[0] = iova; + for (i = 1; i < pa->pa_nr; i++) + pa->pa_iova[i] = pa->pa_iova[i - 1] + PAGE_SIZE; } if (ccw_does_data_transfer(ccw)) { From b21f9cb1124e9fee33dd3c07108aabde060b6ef8 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Mon, 31 Oct 2022 19:12:54 +0100 Subject: [PATCH 076/182] vfio/ccw: refactor the idaw counter The rules of an IDAW are fairly simple: Each one can move no more than a defined amount of data, must not cross the boundary defined by that length, and must be aligned to that length as well. The first IDAW in a list is special, in that it does not need to adhere to that alignment, but the other rules still apply. Thus, by reading the first IDAW in a list, the number of IDAWs that will comprise a data transfer of a particular size can be calculated. Let's factor out the reading of that first IDAW with the logic that calculates the length of the list, to simplify the rest of the routine that handles the individual IDAWs. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 39 ++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index f448aa93007f..9d74e0b74da7 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -496,23 +496,25 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw, return -EFAULT; } -static int ccwchain_fetch_ccw(struct ccw1 *ccw, - struct page_array *pa, - struct channel_program *cp) +/* + * ccw_count_idaws() - Calculate the number of IDAWs needed to transfer + * a specified amount of data + * + * @ccw: The Channel Command Word being translated + * @cp: Channel Program being processed + */ +static int ccw_count_idaws(struct ccw1 *ccw, + struct channel_program *cp) { struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; u64 iova; - unsigned long *idaws; int ret; int bytes = 1; - int idaw_nr, idal_len; - int i; if (ccw->count) bytes = ccw->count; - /* Calculate size of IDAL */ if (ccw_is_idal(ccw)) { /* Read first IDAW to see if it's 4K-aligned or not. */ /* All subsequent IDAws will be 4K-aligned. */ @@ -522,7 +524,26 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, } else { iova = ccw->cda; } - idaw_nr = idal_nr_words((void *)iova, bytes); + + return idal_nr_words((void *)iova, bytes); +} + +static int ccwchain_fetch_ccw(struct ccw1 *ccw, + struct page_array *pa, + struct channel_program *cp) +{ + struct vfio_device *vdev = + &container_of(cp, struct vfio_ccw_private, cp)->vdev; + unsigned long *idaws; + int ret; + int idaw_nr, idal_len; + int i; + + /* Calculate size of IDAL */ + idaw_nr = ccw_count_idaws(ccw, cp); + if (idaw_nr < 0) + return idaw_nr; + idal_len = idaw_nr * sizeof(*idaws); /* Allocate an IDAL from host storage */ @@ -555,7 +576,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, for (i = 0; i < idaw_nr; i++) pa->pa_iova[i] = idaws[i]; } else { - pa->pa_iova[0] = iova; + pa->pa_iova[0] = ccw->cda; for (i = 1; i < pa->pa_nr; i++) pa->pa_iova[i] = pa->pa_iova[i - 1] + PAGE_SIZE; } From 667e5dbabf2bb790640525cff7d563cf88eb3e61 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Thu, 20 Oct 2022 19:00:14 +0200 Subject: [PATCH 077/182] vfio/ccw: read only one Format-1 IDAW The intention is to read the first IDAW to determine the starting location of an I/O operation, knowing that the second and any/all subsequent IDAWs will be aligned per architecture. But, this read receives 64-bits of data, which is the size of a Format-2 IDAW. In the event that Format-1 IDAWs are presented, adjust the size of the read to 32-bits. The data will end up occupying the upper word of the target iova variable, so shift it down to the lower word for use as an address. (By definition, this IDAW format uses a 31-bit address, so the "sign" bit will always be off and there is no concern about sign extension.) Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 9d74e0b74da7..fbb46bec3174 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -509,6 +509,7 @@ static int ccw_count_idaws(struct ccw1 *ccw, struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; u64 iova; + int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32); int ret; int bytes = 1; @@ -516,11 +517,18 @@ static int ccw_count_idaws(struct ccw1 *ccw, bytes = ccw->count; if (ccw_is_idal(ccw)) { - /* Read first IDAW to see if it's 4K-aligned or not. */ - /* All subsequent IDAws will be 4K-aligned. */ - ret = vfio_dma_rw(vdev, ccw->cda, &iova, sizeof(iova), false); + /* Read first IDAW to check its starting address. */ + /* All subsequent IDAWs will be 2K- or 4K-aligned. */ + ret = vfio_dma_rw(vdev, ccw->cda, &iova, size, false); if (ret) return ret; + + /* + * Format-1 IDAWs only occupy the first 32 bits, + * and bit 0 is always off. + */ + if (!cp->orb.cmd.c64) + iova = iova >> 32; } else { iova = ccw->cda; } From 6a6dc14ac84733cf5864a7cf9f5b3e43f6a79be8 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Wed, 2 Dec 2020 19:37:24 +0100 Subject: [PATCH 078/182] vfio/ccw: calculate number of IDAWs regardless of format The idal_nr_words() routine works well for 4K IDAWs, but lost its ability to handle the old 2K formats with the removal of 31-bit builds in commit 5a79859ae0f3 ("s390: remove 31 bit support"). Since there's nothing preventing a guest from generating this IDAW format, let's re-introduce the math for them and use both when calculating the number of IDAWs based on the bits specified in the ORB. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- arch/s390/include/asm/idals.h | 12 ++++++++++++ drivers/s390/cio/vfio_ccw_cp.c | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/arch/s390/include/asm/idals.h b/arch/s390/include/asm/idals.h index 40eae2c08d61..59fcc3c72edf 100644 --- a/arch/s390/include/asm/idals.h +++ b/arch/s390/include/asm/idals.h @@ -23,6 +23,9 @@ #define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */ #define IDA_BLOCK_SIZE (1L<> IDA_SIZE_LOG; } +/* + * Return the number of 2K IDA words needed for an address/length pair. + */ +static inline unsigned int idal_2k_nr_words(void *vaddr, unsigned int length) +{ + return ((__pa(vaddr) & (IDA_2K_BLOCK_SIZE - 1)) + length + + (IDA_2K_BLOCK_SIZE - 1)) >> IDA_2K_SIZE_LOG; +} + /* * Create the list of idal words for an address/length pair. */ diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index fbb46bec3174..6a2c6ee83807 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -502,6 +502,13 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw, * * @ccw: The Channel Command Word being translated * @cp: Channel Program being processed + * + * The ORB is examined, since it specifies what IDAWs could actually be + * used by any CCW in the channel program, regardless of whether or not + * the CCW actually does. An ORB that does not specify Format-2-IDAW + * Control could still contain a CCW with an IDAL, which would be + * Format-1 and thus only move 2K with each IDAW. Thus all CCWs within + * the channel program must follow the same size requirements. */ static int ccw_count_idaws(struct ccw1 *ccw, struct channel_program *cp) @@ -533,6 +540,15 @@ static int ccw_count_idaws(struct ccw1 *ccw, iova = ccw->cda; } + /* Format-1 IDAWs operate on 2K each */ + if (!cp->orb.cmd.c64) + return idal_2k_nr_words((void *)iova, bytes); + + /* Using the 2K variant of Format-2 IDAWs? */ + if (cp->orb.cmd.i2k) + return idal_2k_nr_words((void *)iova, bytes); + + /* The 'usual' case is 4K Format-2 IDAWs */ return idal_nr_words((void *)iova, bytes); } From 61f3a16b9d5cd9361a317ee7870083c1bc171188 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 21 Oct 2022 20:53:51 +0200 Subject: [PATCH 079/182] vfio/ccw: allocate/populate the guest idal Today, we allocate memory for a list of IDAWs, and if the CCW being processed contains an IDAL we read that data from the guest into that space. We then copy each IDAW into the pa_iova array, or fabricate that pa_iova array with a list of addresses based on a direct-addressed CCW. Combine the reading of the guest IDAL with the creation of a pseudo-IDAL for direct-addressed CCWs, so that both CCW types have a "guest" IDAL that can be populated straight into the pa_iova array. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 76 +++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 6a2c6ee83807..525277750041 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -192,11 +192,12 @@ static inline void page_array_idal_create_words(struct page_array *pa, * idaw. */ - for (i = 0; i < pa->pa_nr; i++) + for (i = 0; i < pa->pa_nr; i++) { idaws[i] = page_to_phys(pa->pa_page[i]); - /* Adjust the first IDAW, since it may not start on a page boundary */ - idaws[0] += pa->pa_iova[0] & (PAGE_SIZE - 1); + /* Incorporate any offset from each starting address */ + idaws[i] += pa->pa_iova[i] & (PAGE_SIZE - 1); + } } static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) @@ -496,6 +497,44 @@ static int ccwchain_fetch_tic(struct ccw1 *ccw, return -EFAULT; } +static unsigned long *get_guest_idal(struct ccw1 *ccw, + struct channel_program *cp, + int idaw_nr) +{ + struct vfio_device *vdev = + &container_of(cp, struct vfio_ccw_private, cp)->vdev; + unsigned long *idaws; + int idal_len = idaw_nr * sizeof(*idaws); + int idaw_size = PAGE_SIZE; + int idaw_mask = ~(idaw_size - 1); + int i, ret; + + idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL); + if (!idaws) + return ERR_PTR(-ENOMEM); + + if (ccw_is_idal(ccw)) { + /* Copy IDAL from guest */ + ret = vfio_dma_rw(vdev, ccw->cda, idaws, idal_len, false); + if (ret) { + kfree(idaws); + return ERR_PTR(ret); + } + } else { + /* Fabricate an IDAL based off CCW data address */ + if (cp->orb.cmd.c64) { + idaws[0] = ccw->cda; + for (i = 1; i < idaw_nr; i++) + idaws[i] = (idaws[i - 1] + idaw_size) & idaw_mask; + } else { + kfree(idaws); + return ERR_PTR(-EOPNOTSUPP); + } + } + + return idaws; +} + /* * ccw_count_idaws() - Calculate the number of IDAWs needed to transfer * a specified amount of data @@ -560,7 +599,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, &container_of(cp, struct vfio_ccw_private, cp)->vdev; unsigned long *idaws; int ret; - int idaw_nr, idal_len; + int idaw_nr; int i; /* Calculate size of IDAL */ @@ -568,12 +607,10 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, if (idaw_nr < 0) return idaw_nr; - idal_len = idaw_nr * sizeof(*idaws); - /* Allocate an IDAL from host storage */ - idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL); - if (!idaws) { - ret = -ENOMEM; + idaws = get_guest_idal(ccw, cp, idaw_nr); + if (IS_ERR(idaws)) { + ret = PTR_ERR(idaws); goto out_init; } @@ -587,22 +624,13 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, if (ret < 0) goto out_free_idaws; - if (ccw_is_idal(ccw)) { - /* Copy guest IDAL into host IDAL */ - ret = vfio_dma_rw(vdev, ccw->cda, idaws, idal_len, false); - if (ret) - goto out_unpin; - - /* - * Copy guest IDAWs into page_array, in case the memory they - * occupy is not contiguous. - */ - for (i = 0; i < idaw_nr; i++) + /* + * Copy guest IDAWs into page_array, in case the memory they + * occupy is not contiguous. + */ + for (i = 0; i < idaw_nr; i++) { + if (cp->orb.cmd.c64) pa->pa_iova[i] = idaws[i]; - } else { - pa->pa_iova[0] = ccw->cda; - for (i = 1; i < pa->pa_nr; i++) - pa->pa_iova[i] = pa->pa_iova[i - 1] + PAGE_SIZE; } if (ccw_does_data_transfer(ccw)) { From 1b676fe3d9d3f262bc26bb18dc1b1ac66c83c2a0 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Mon, 31 Oct 2022 19:48:29 +0100 Subject: [PATCH 080/182] vfio/ccw: handle a guest Format-1 IDAL There are two scenarios that need to be addressed here. First, an ORB that does NOT have the Format-2 IDAL bit set could have both a direct-addressed CCW and an indirect-data-address CCW chained together. This means that the IDA CCW will contain a Format-1 IDAL, and can be easily converted to a 2K Format-2 IDAL. But it also means that the direct-addressed CCW needs to be converted to the same 2K Format-2 IDAL for consistency with the ORB settings. Secondly, a Format-1 IDAL is comprised of 31-bit addresses. Thus, we need to cast this IDAL to a pointer of ints while populating the list of addresses that are sent to vfio. Since the result of both of these is the use of the 2K IDAL variants, and the output of vfio-ccw is always a Format-2 IDAL (in order to use 64-bit addresses), make sure that the correct control bit gets set in the ORB when these scenarios occur. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 525277750041..a7415d440a81 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -222,6 +222,8 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) } } +#define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k) + /* * Helpers to operate ccwchain. */ @@ -504,8 +506,9 @@ static unsigned long *get_guest_idal(struct ccw1 *ccw, struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; unsigned long *idaws; + unsigned int *idaws_f1; int idal_len = idaw_nr * sizeof(*idaws); - int idaw_size = PAGE_SIZE; + int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE; int idaw_mask = ~(idaw_size - 1); int i, ret; @@ -527,8 +530,10 @@ static unsigned long *get_guest_idal(struct ccw1 *ccw, for (i = 1; i < idaw_nr; i++) idaws[i] = (idaws[i - 1] + idaw_size) & idaw_mask; } else { - kfree(idaws); - return ERR_PTR(-EOPNOTSUPP); + idaws_f1 = (unsigned int *)idaws; + idaws_f1[0] = ccw->cda; + for (i = 1; i < idaw_nr; i++) + idaws_f1[i] = (idaws_f1[i - 1] + idaw_size) & idaw_mask; } } @@ -598,6 +603,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; unsigned long *idaws; + unsigned int *idaws_f1; int ret; int idaw_nr; int i; @@ -628,9 +634,12 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, * Copy guest IDAWs into page_array, in case the memory they * occupy is not contiguous. */ + idaws_f1 = (unsigned int *)idaws; for (i = 0; i < idaw_nr; i++) { if (cp->orb.cmd.c64) pa->pa_iova[i] = idaws[i]; + else + pa->pa_iova[i] = idaws_f1[i]; } if (ccw_does_data_transfer(ccw)) { @@ -851,7 +860,11 @@ union orb *cp_get_orb(struct channel_program *cp, struct subchannel *sch) /* * Everything built by vfio-ccw is a Format-2 IDAL. + * If the input was a Format-1 IDAL, indicate that + * 2K Format-2 IDAWs were created here. */ + if (!orb->cmd.c64) + orb->cmd.i2k = 1; orb->cmd.c64 = 1; if (orb->cmd.lpm == 0) From b5a73e8eb225e3103a030c518375b4b2d0c66ccd Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Thu, 11 Aug 2022 19:20:54 +0200 Subject: [PATCH 081/182] vfio/ccw: don't group contiguous pages on 2K IDAWs The vfio_pin_pages() interface allows contiguous pages to be pinned as a single request, which is great for the 4K pages that are normally processed. Old IDA formats operate on 2K chunks, which makes this logic more difficult. Since these formats are rare, let's just invoke the page pinning one-at-a-time, instead of trying to group them. We can rework this code at a later date if needed. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_cp.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index a7415d440a81..1500058dbe1f 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -83,12 +83,13 @@ static int page_array_alloc(struct page_array *pa, unsigned int len) * @pa: page_array on which to perform the operation * @vdev: the vfio device to perform the operation * @pa_nr: number of user pages to unpin + * @unaligned: were pages unaligned on the pin request * * Only unpin if any pages were pinned to begin with, i.e. pa_nr > 0, * otherwise only clear pa->pa_nr */ static void page_array_unpin(struct page_array *pa, - struct vfio_device *vdev, int pa_nr) + struct vfio_device *vdev, int pa_nr, bool unaligned) { int unpinned = 0, npage = 1; @@ -97,7 +98,8 @@ static void page_array_unpin(struct page_array *pa, dma_addr_t *last = &first[npage]; if (unpinned + npage < pa_nr && - *first + npage * PAGE_SIZE == *last) { + *first + npage * PAGE_SIZE == *last && + !unaligned) { npage++; continue; } @@ -114,12 +116,19 @@ static void page_array_unpin(struct page_array *pa, * page_array_pin() - Pin user pages in memory * @pa: page_array on which to perform the operation * @vdev: the vfio device to perform pin operations + * @unaligned: are pages aligned to 4K boundary? * * Returns number of pages pinned upon success. * If the pin request partially succeeds, or fails completely, * all pages are left unpinned and a negative error value is returned. + * + * Requests to pin "aligned" pages can be coalesced into a single + * vfio_pin_pages request for the sake of efficiency, based on the + * expectation of 4K page requests. Unaligned requests are probably + * dealing with 2K "pages", and cannot be coalesced without + * reworking this logic to incorporate that math. */ -static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) +static int page_array_pin(struct page_array *pa, struct vfio_device *vdev, bool unaligned) { int pinned = 0, npage = 1; int ret = 0; @@ -129,7 +138,8 @@ static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) dma_addr_t *last = &first[npage]; if (pinned + npage < pa->pa_nr && - *first + npage * PAGE_SIZE == *last) { + *first + npage * PAGE_SIZE == *last && + !unaligned) { npage++; continue; } @@ -151,14 +161,14 @@ static int page_array_pin(struct page_array *pa, struct vfio_device *vdev) return ret; err_out: - page_array_unpin(pa, vdev, pinned); + page_array_unpin(pa, vdev, pinned, unaligned); return ret; } /* Unpin the pages before releasing the memory. */ -static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vdev) +static void page_array_unpin_free(struct page_array *pa, struct vfio_device *vdev, bool unaligned) { - page_array_unpin(pa, vdev, pa->pa_nr); + page_array_unpin(pa, vdev, pa->pa_nr, unaligned); kfree(pa->pa_page); kfree(pa->pa_iova); } @@ -643,7 +653,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, } if (ccw_does_data_transfer(ccw)) { - ret = page_array_pin(pa, vdev); + ret = page_array_pin(pa, vdev, idal_is_2k(cp)); if (ret < 0) goto out_unpin; } else { @@ -659,7 +669,7 @@ static int ccwchain_fetch_ccw(struct ccw1 *ccw, return 0; out_unpin: - page_array_unpin_free(pa, vdev); + page_array_unpin_free(pa, vdev, idal_is_2k(cp)); out_free_idaws: kfree(idaws); out_init: @@ -757,7 +767,7 @@ void cp_free(struct channel_program *cp) cp->initialized = false; list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) { - page_array_unpin_free(&chain->ch_pa[i], vdev); + page_array_unpin_free(&chain->ch_pa[i], vdev, idal_is_2k(cp)); ccwchain_cda_free(chain, i); } ccwchain_free(chain); From beb060ed20d5d5a54754cf78c38731a6a5cb0f18 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 19 Feb 2021 20:41:49 +0100 Subject: [PATCH 082/182] vfio/ccw: remove old IDA format restrictions By this point, all the pieces are in place to properly support a 2K Format-2 IDAL, and to convert a guest Format-1 IDAL to the 2K Format-2 variety. Let's remove the fence that prohibits them, and allow a guest to submit them if desired. Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Heiko Carstens --- Documentation/s390/vfio-ccw.rst | 4 ++-- drivers/s390/cio/vfio_ccw_cp.c | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Documentation/s390/vfio-ccw.rst b/Documentation/s390/vfio-ccw.rst index ea928a3806f4..a11c24701dcd 100644 --- a/Documentation/s390/vfio-ccw.rst +++ b/Documentation/s390/vfio-ccw.rst @@ -219,8 +219,8 @@ values may occur: The operation was successful. ``-EOPNOTSUPP`` - The orb specified transport mode or an unidentified IDAW format, or the - scsw specified a function other than the start function. + The ORB specified transport mode or the + SCSW specified a function other than the start function. ``-EIO`` A request was issued while the device was not in a state ready to accept diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 1500058dbe1f..1c31e81ca8de 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -380,14 +380,6 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp) do { cnt++; - /* - * As we don't want to fail direct addressing even if the - * orb specified one of the unsupported formats, we defer - * checking for IDAWs in unsupported formats to here. - */ - if ((!cp->orb.cmd.c64 || cp->orb.cmd.i2k) && ccw_is_idal(ccw)) - return -EOPNOTSUPP; - /* * We want to keep counting if the current CCW has the * command-chaining flag enabled, or if it is a TIC CCW From a43e3115fbea2f9ba040a183aab300e0abf9cb67 Mon Sep 17 00:00:00 2001 From: Xu Panda Date: Thu, 5 Jan 2023 20:24:34 +0800 Subject: [PATCH 083/182] s390/zcrypt: use strscpy() to instead of strncpy() The implementation of strscpy() is more robust and safer. That's now the recommended way to copy NUL-terminated strings. Signed-off-by: Xu Panda Signed-off-by: Yang Yang Link: https://lore.kernel.org/r/202301052024349365834@zte.com.cn Signed-off-by: Heiko Carstens --- drivers/s390/crypto/zcrypt_api.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 4bf36e53fe3e..6fe05bb82c77 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -347,8 +347,7 @@ static ssize_t zcdn_create_store(struct class *class, int rc; char name[ZCDN_MAX_NAME]; - strncpy(name, skip_spaces(buf), sizeof(name)); - name[sizeof(name) - 1] = '\0'; + strscpy(name, skip_spaces(buf), sizeof(name)); rc = zcdn_create(strim(name)); @@ -365,8 +364,7 @@ static ssize_t zcdn_destroy_store(struct class *class, int rc; char name[ZCDN_MAX_NAME]; - strncpy(name, skip_spaces(buf), sizeof(name)); - name[sizeof(name) - 1] = '\0'; + strscpy(name, skip_spaces(buf), sizeof(name)); rc = zcdn_destroy(strim(name)); From 9cab4f7d98eee90b762ffbc87055c9499d51a635 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 10 Jan 2023 13:49:23 +0100 Subject: [PATCH 084/182] s390/con3270: move condev definition Fix this for allmodconfig: drivers/s390/char/con3270.c:43:24: error: 'condev' defined but not used [-Werror=unused-variable] static struct tty3270 *condev; ^~~~~~ Reported-by: Stephen Rothwell Fixes: c17fe081ac1f ("s390/3270: unify con3270 + tty3270") Signed-off-by: Heiko Carstens --- drivers/s390/char/con3270.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 9402690de598..d9983550062d 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -40,7 +40,6 @@ static struct tty_driver *tty3270_driver; static int tty3270_max_index; -static struct tty3270 *condev; static struct raw3270_fn tty3270_fn; #define TTY3270_HIGHLIGHT_BLINK 1 @@ -2045,6 +2044,9 @@ static void __exit tty3270_exit(void) } #if IS_ENABLED(CONFIG_TN3270_CONSOLE) + +static struct tty3270 *condev; + static void con3270_write(struct console *co, const char *str, unsigned int count) { From e7b481697188f2c715b951b4aa771a8ffebf3243 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 8 Jan 2023 19:13:49 +0100 Subject: [PATCH 085/182] s390/archrandom: add missing header include Add missing header include to get rid of arch/s390/crypto/arch_random.c:15:1: warning: symbol 's390_arch_random_available' was not declared. Should it be static? arch/s390/crypto/arch_random.c:17:12: warning: symbol 's390_arch_random_counter' was not declared. Should it be static? Signed-off-by: Heiko Carstens --- arch/s390/crypto/arch_random.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/crypto/arch_random.c b/arch/s390/crypto/arch_random.c index 1f2d40993c4d..a8a2407381af 100644 --- a/arch/s390/crypto/arch_random.c +++ b/arch/s390/crypto/arch_random.c @@ -10,6 +10,7 @@ #include #include #include +#include #include DEFINE_STATIC_KEY_FALSE(s390_arch_random_available); From 9c3205b2b062420c26b33924b910880889acf832 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 5 May 2022 16:54:54 +0200 Subject: [PATCH 086/182] s390/boot: cleanup decompressor header files Move declarations to appropriate header files. Instead of cryptic casting directly assign struct vmlinux_info type to _vmlinux_info linker script variable - wich it actually is. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 24 ++++++++++++++++++++++-- arch/s390/boot/decompressor.c | 1 + arch/s390/boot/decompressor.h | 26 -------------------------- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 70418389414d..f6e82cf7851e 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -8,10 +8,26 @@ #ifndef __ASSEMBLY__ +struct vmlinux_info { + unsigned long default_lma; + void (*entry)(void); + unsigned long image_size; /* does not include .bss */ + unsigned long bss_size; /* uncompressed image .bss size */ + unsigned long bootdata_off; + unsigned long bootdata_size; + unsigned long bootdata_preserved_off; + unsigned long bootdata_preserved_size; + unsigned long dynsym_start; + unsigned long rela_dyn_start; + unsigned long rela_dyn_end; + unsigned long amode31_size; +}; + void startup_kernel(void); unsigned long detect_memory(void); bool is_ipl_block_dump(void); void store_ipl_parmblock(void); +unsigned long read_ipl_report(unsigned long safe_offset); void setup_boot_command_line(void); void parse_boot_command_line(void); void verify_facilities(void); @@ -20,6 +36,7 @@ void sclp_early_setup_buffer(void); void print_pgm_check_info(void); unsigned long get_random_base(unsigned long safe_addr); void __printf(1, 2) decompressor_printk(const char *fmt, ...); +void error(char *m); /* Symbols defined by linker scripts */ extern const char kernel_version[]; @@ -31,8 +48,11 @@ extern char __boot_data_start[], __boot_data_end[]; extern char __boot_data_preserved_start[], __boot_data_preserved_end[]; extern char _decompressor_syms_start[], _decompressor_syms_end[]; extern char _stack_start[], _stack_end[]; - -unsigned long read_ipl_report(unsigned long safe_offset); +extern char _end[]; +extern unsigned char _compressed_start[]; +extern unsigned char _compressed_end[]; +extern struct vmlinux_info _vmlinux_info; +#define vmlinux _vmlinux_info #endif /* __ASSEMBLY__ */ #endif /* BOOT_BOOT_H */ diff --git a/arch/s390/boot/decompressor.c b/arch/s390/boot/decompressor.c index e27c2140d620..4c8c82a951b7 100644 --- a/arch/s390/boot/decompressor.c +++ b/arch/s390/boot/decompressor.c @@ -11,6 +11,7 @@ #include #include #include "decompressor.h" +#include "boot.h" /* * gzip declarations diff --git a/arch/s390/boot/decompressor.h b/arch/s390/boot/decompressor.h index f75cc31a77dd..92b81d2ea35d 100644 --- a/arch/s390/boot/decompressor.h +++ b/arch/s390/boot/decompressor.h @@ -2,37 +2,11 @@ #ifndef BOOT_COMPRESSED_DECOMPRESSOR_H #define BOOT_COMPRESSED_DECOMPRESSOR_H -#include - #ifdef CONFIG_KERNEL_UNCOMPRESSED static inline void *decompress_kernel(void) { return NULL; } #else void *decompress_kernel(void); #endif unsigned long mem_safe_offset(void); -void error(char *m); - -struct vmlinux_info { - unsigned long default_lma; - void (*entry)(void); - unsigned long image_size; /* does not include .bss */ - unsigned long bss_size; /* uncompressed image .bss size */ - unsigned long bootdata_off; - unsigned long bootdata_size; - unsigned long bootdata_preserved_off; - unsigned long bootdata_preserved_size; - unsigned long dynsym_start; - unsigned long rela_dyn_start; - unsigned long rela_dyn_end; - unsigned long amode31_size; -}; - -/* Symbols defined by linker scripts */ -extern char _end[]; -extern unsigned char _compressed_start[]; -extern unsigned char _compressed_end[]; -extern char _vmlinux_info[]; - -#define vmlinux (*(struct vmlinux_info *)_vmlinux_info) #endif /* BOOT_COMPRESSED_DECOMPRESSOR_H */ From 639886b71ddef085a0e7bb1f225b8ae3eda5c06f Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 15 Dec 2022 08:00:34 +0100 Subject: [PATCH 087/182] s390/early: fix sclp_early_sccb variable lifetime Commit ada1da31ce34 ("s390/sclp: sort out physical vs virtual pointers usage") fixed the notion of virtual address for sclp_early_sccb pointer. However, it did not take into account that kasan_early_init() can also output messages and sclp_early_sccb should be adjusted by the time kasan_early_init() is called. Currently it is not a problem, since virtual and physical addresses on s390 are the same. Nevertheless, should they ever differ, this would cause an invalid pointer access. Fixes: ada1da31ce34 ("s390/sclp: sort out physical vs virtual pointers usage") Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/kernel/early.c | 1 - arch/s390/kernel/head64.S | 1 + drivers/s390/char/sclp_early.c | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 6030fdd6997b..9693c8630e73 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -288,7 +288,6 @@ static void __init sort_amode31_extable(void) void __init startup_init(void) { - sclp_early_adjust_va(); reset_tod_clock(); check_image_bootable(); time_early_init(); diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index d7b8b6ad574d..3b3bf8329e6c 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -25,6 +25,7 @@ ENTRY(startup_continue) larl %r14,init_task stg %r14,__LC_CURRENT larl %r15,init_thread_union+THREAD_SIZE-STACK_FRAME_OVERHEAD-__PT_SIZE + brasl %r14,sclp_early_adjust_va # allow sclp_early_printk #ifdef CONFIG_KASAN brasl %r14,kasan_early_init #endif diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index c1c70a161c0e..f480d6c7fd39 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -163,7 +163,7 @@ static void __init sclp_early_console_detect(struct init_sccb *sccb) sclp.has_linemode = 1; } -void __init sclp_early_adjust_va(void) +void __init __no_sanitize_address sclp_early_adjust_va(void) { sclp_early_sccb = __va((unsigned long)sclp_early_sccb); } From aae2f753d2a96fb062c3cb710ccbd4cb3d5b9452 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Tue, 13 Dec 2022 11:31:39 +0100 Subject: [PATCH 088/182] s390/kasan: sort out physical vs virtual memory confusion The kasan early boot memory allocators operate on pgalloc_pos and segment_pos physical address pointers, but fail to convert it to the corresponding virtual pointers. Currently it is not a problem, since virtual and physical addresses on s390 are the same. Nevertheless, should they ever differ, this would cause an invalid pointer access. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/kasan_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 9f988d4582ed..c9674f83ed0c 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -39,7 +39,7 @@ static void * __init kasan_early_alloc_segment(void) if (segment_pos < segment_low) kasan_early_panic("out of memory during initialisation\n"); - return (void *)segment_pos; + return __va(segment_pos); } static void * __init kasan_early_alloc_pages(unsigned int order) @@ -49,7 +49,7 @@ static void * __init kasan_early_alloc_pages(unsigned int order) if (pgalloc_pos < pgalloc_low) kasan_early_panic("out of memory during initialisation\n"); - return (void *)pgalloc_pos; + return __va(pgalloc_pos); } static void * __init kasan_early_crst_alloc(unsigned long val) From 8772555ad0e43c31cb5e29f33b23c7c11ecd5a9d Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 9 Dec 2022 22:09:44 +0100 Subject: [PATCH 089/182] s390/kasan: cleanup setup of zero pgtable Fix variables initialization coding style and setup zero pgtable same way region and segment pgtables are set up. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/kasan_init.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index c9674f83ed0c..6fbc1dc63d3a 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -251,19 +251,17 @@ static void __init kasan_early_detect_facilities(void) void __init kasan_early_init(void) { - unsigned long shadow_alloc_size; - unsigned long initrd_end; - unsigned long memsize; - unsigned long pgt_prot = pgprot_val(PAGE_KERNEL_RO); - pte_t pte_z; + pte_t pte_z = __pte(__pa(kasan_early_shadow_page) | pgprot_val(PAGE_KERNEL_RO)); pmd_t pmd_z = __pmd(__pa(kasan_early_shadow_pte) | _SEGMENT_ENTRY); pud_t pud_z = __pud(__pa(kasan_early_shadow_pmd) | _REGION3_ENTRY); p4d_t p4d_z = __p4d(__pa(kasan_early_shadow_pud) | _REGION2_ENTRY); + unsigned long shadow_alloc_size; + unsigned long initrd_end; + unsigned long memsize; kasan_early_detect_facilities(); if (!has_nx) - pgt_prot &= ~_PAGE_NOEXEC; - pte_z = __pte(__pa(kasan_early_shadow_page) | pgt_prot); + pte_z = clear_pte_bit(pte_z, __pgprot(_PAGE_NOEXEC)); memsize = get_mem_detect_end(); if (!memsize) From e148071b9f7711cb68c6d89c5b2033ee5a8add93 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Sat, 10 Dec 2022 09:49:04 +0100 Subject: [PATCH 090/182] s390/kasan: cleanup setup of untracked memory pgtables Avoid duplicate IS_ENABLED(CONFIG_KASAN_VMALLOC) condition check. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/kasan_init.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 6fbc1dc63d3a..89545280935a 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -255,6 +255,7 @@ void __init kasan_early_init(void) pmd_t pmd_z = __pmd(__pa(kasan_early_shadow_pte) | _SEGMENT_ENTRY); pud_t pud_z = __pud(__pa(kasan_early_shadow_pmd) | _REGION3_ENTRY); p4d_t p4d_z = __p4d(__pa(kasan_early_shadow_pud) | _REGION2_ENTRY); + unsigned long untracked_end = MODULES_VADDR; unsigned long shadow_alloc_size; unsigned long initrd_end; unsigned long memsize; @@ -350,15 +351,13 @@ void __init kasan_early_init(void) /* populate kasan shadow (for identity mapping and zero page mapping) */ kasan_early_pgtable_populate(__sha(0), __sha(memsize), POPULATE_MAP); if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { + untracked_end = VMALLOC_START; /* shallowly populate kasan shadow for vmalloc and modules */ kasan_early_pgtable_populate(__sha(VMALLOC_START), __sha(MODULES_END), POPULATE_SHALLOW); } /* populate kasan shadow for untracked memory */ - kasan_early_pgtable_populate(__sha(ident_map_size), - IS_ENABLED(CONFIG_KASAN_VMALLOC) ? - __sha(VMALLOC_START) : - __sha(MODULES_VADDR), + kasan_early_pgtable_populate(__sha(ident_map_size), __sha(untracked_end), POPULATE_ZERO_SHADOW); kasan_early_pgtable_populate(__sha(MODULES_END), __sha(_REGION1_SIZE), POPULATE_ZERO_SHADOW); From bf2b4af2ed23b9548ea7f24613fd905f56ec7910 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 16 Dec 2022 19:07:38 +0100 Subject: [PATCH 091/182] s390/kasan: use set_pXe_bit() for pgtable entries setup Convert setup of pgtable entries to use set_pXe_bit() helpers as the preferred way in MM code. Locally introduce pgprot_clear_bit() helper, which is strictly speaking a generic function. However, it is only x86 pgprot_clear_protnone_bits() helper, which does a similar thing, so do not make it public. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/kasan_init.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 89545280935a..a97b7981358e 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -86,25 +86,32 @@ enum populate_mode { POPULATE_ZERO_SHADOW, POPULATE_SHALLOW }; + +static inline pgprot_t pgprot_clear_bit(pgprot_t pgprot, unsigned long bit) +{ + return __pgprot(pgprot_val(pgprot) & ~bit); +} + static void __init kasan_early_pgtable_populate(unsigned long address, unsigned long end, enum populate_mode mode) { - unsigned long pgt_prot_zero, pgt_prot, sgt_prot; + pgprot_t pgt_prot_zero = PAGE_KERNEL_RO; + pgprot_t pgt_prot = PAGE_KERNEL; + pgprot_t sgt_prot = SEGMENT_KERNEL; pgd_t *pg_dir; p4d_t *p4_dir; pud_t *pu_dir; pmd_t *pm_dir; pte_t *pt_dir; + pmd_t pmd; + pte_t pte; - pgt_prot_zero = pgprot_val(PAGE_KERNEL_RO); if (!has_nx) - pgt_prot_zero &= ~_PAGE_NOEXEC; - pgt_prot = pgprot_val(PAGE_KERNEL); - sgt_prot = pgprot_val(SEGMENT_KERNEL); + pgt_prot_zero = pgprot_clear_bit(pgt_prot_zero, _PAGE_NOEXEC); if (!has_nx || mode == POPULATE_ONE2ONE) { - pgt_prot &= ~_PAGE_NOEXEC; - sgt_prot &= ~_SEGMENT_ENTRY_NOEXEC; + pgt_prot = pgprot_clear_bit(pgt_prot, _PAGE_NOEXEC); + sgt_prot = pgprot_clear_bit(sgt_prot, _SEGMENT_ENTRY_NOEXEC); } /* @@ -175,7 +182,9 @@ static void __init kasan_early_pgtable_populate(unsigned long address, page = kasan_early_alloc_segment(); memset(page, 0, _SEGMENT_SIZE); } - set_pmd(pm_dir, __pmd(__pa(page) | sgt_prot)); + pmd = __pmd(__pa(page)); + pmd = set_pmd_bit(pmd, sgt_prot); + set_pmd(pm_dir, pmd); address = (address + PMD_SIZE) & PMD_MASK; continue; } @@ -194,16 +203,22 @@ static void __init kasan_early_pgtable_populate(unsigned long address, switch (mode) { case POPULATE_ONE2ONE: page = (void *)address; - set_pte(pt_dir, __pte(__pa(page) | pgt_prot)); + pte = __pte(__pa(page)); + pte = set_pte_bit(pte, pgt_prot); + set_pte(pt_dir, pte); break; case POPULATE_MAP: page = kasan_early_alloc_pages(0); memset(page, 0, PAGE_SIZE); - set_pte(pt_dir, __pte(__pa(page) | pgt_prot)); + pte = __pte(__pa(page)); + pte = set_pte_bit(pte, pgt_prot); + set_pte(pt_dir, pte); break; case POPULATE_ZERO_SHADOW: page = kasan_early_shadow_page; - set_pte(pt_dir, __pte(__pa(page) | pgt_prot_zero)); + pte = __pte(__pa(page)); + pte = set_pte_bit(pte, pgt_prot_zero); + set_pte(pt_dir, pte); break; case POPULATE_SHALLOW: /* should never happen */ From b265854060525ca2cef74e40a49494bbb52700e6 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 2 Dec 2022 19:07:07 +0100 Subject: [PATCH 092/182] s390/pgtable: add REGION3_KERNEL_EXEC protection Similar to existing PAGE_KERNEL_EXEC and SEGMENT_KERNEL_EXEC memory protection add REGION3_KERNEL_EXEC attribute that could be set on PUD pgtable entries. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pgtable.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index b26cbf1c533c..0170f95f3b91 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -477,6 +477,12 @@ static inline int is_module_addr(void *addr) _REGION3_ENTRY_YOUNG | \ _REGION_ENTRY_PROTECT | \ _REGION_ENTRY_NOEXEC) +#define REGION3_KERNEL_EXEC __pgprot(_REGION_ENTRY_TYPE_R3 | \ + _REGION3_ENTRY_LARGE | \ + _REGION3_ENTRY_READ | \ + _REGION3_ENTRY_WRITE | \ + _REGION3_ENTRY_YOUNG | \ + _REGION3_ENTRY_DIRTY) static inline bool mm_p4d_folded(struct mm_struct *mm) { From bd50b7436217b4123911c2bca1efd74718654f06 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Sun, 4 Dec 2022 21:15:41 +0100 Subject: [PATCH 093/182] s390/boot: detect and enable memory facilities Detect and enable memory facilities which is a prerequisite for pgtables setup in the decompressor. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 8 ++++++++ arch/s390/boot/startup.c | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index f6e82cf7851e..286441cf3bf0 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -8,6 +8,12 @@ #ifndef __ASSEMBLY__ +struct machine_info { + unsigned char has_edat1 : 1; + unsigned char has_edat2 : 1; + unsigned char has_nx : 1; +}; + struct vmlinux_info { unsigned long default_lma; void (*entry)(void); @@ -38,6 +44,8 @@ unsigned long get_random_base(unsigned long safe_addr); void __printf(1, 2) decompressor_printk(const char *fmt, ...); void error(char *m); +extern struct machine_info machine; + /* Symbols defined by linker scripts */ extern const char kernel_version[]; extern unsigned long memory_limit; diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 47ca3264c023..da6ee587fe9a 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -33,6 +33,8 @@ u64 __bootdata_preserved(stfle_fac_list[16]); u64 __bootdata_preserved(alt_stfle_fac_list[16]); struct oldmem_data __bootdata_preserved(oldmem_data); +struct machine_info machine; + void error(char *x) { sclp_early_printk("\n\n"); @@ -42,6 +44,20 @@ void error(char *x) disabled_wait(); } +static void detect_facilities(void) +{ + if (test_facility(8)) { + machine.has_edat1 = 1; + __ctl_set_bit(0, 23); + } + if (test_facility(78)) + machine.has_edat2 = 1; + if (!noexec_disabled && test_facility(130)) { + machine.has_nx = 1; + __ctl_set_bit(0, 20); + } +} + static void setup_lpp(void) { S390_lowcore.current_pid = 0; @@ -254,6 +270,8 @@ void startup_kernel(void) unsigned long safe_addr; void *img; + detect_facilities(); + initrd_data.start = parmarea.initrd_start; initrd_data.size = parmarea.initrd_size; oldmem_data.start = parmarea.oldmem_base; From bb1520d581a3a46e2d6e12bb74604ace33404de5 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Tue, 13 Dec 2022 11:35:11 +0100 Subject: [PATCH 094/182] s390/mm: start kernel with DAT enabled The setup of the kernel virtual address space is spread throughout the sources, boot stages and config options like this: 1. The available physical memory regions are queried and stored as mem_detect information for later use in the decompressor. 2. Based on the physical memory availability the virtual memory layout is established in the decompressor; 3. If CONFIG_KASAN is disabled the kernel paging setup code populates kernel pgtables and turns DAT mode on. It uses the information stored at step [1]. 4. If CONFIG_KASAN is enabled the kernel early boot kasan setup populates kernel pgtables and turns DAT mode on. It uses the information stored at step [1]. The kasan setup creates early_pg_dir directory and directly overwrites swapper_pg_dir entries to make shadow memory pages available. Move the kernel virtual memory setup to the decompressor and start the kernel with DAT turned on right from the very first istruction. That completely eliminates the boot phase when the kernel runs in DAT-off mode, simplies the overall design and consolidates pgtables setup. The identity mapping is created in the decompressor, while kasan shadow mappings are still created by the early boot kernel code. Share with decompressor the existing kasan memory allocator. It decreases the size of a newly requested memory block from pgalloc_pos and ensures that kernel image is not overwritten. pgalloc_low and pgalloc_pos pointers are made preserved boot variables for that. Use the bootdata infrastructure to setup swapper_pg_dir and invalid_pg_dir directories used by the kernel later. The interim early_pg_dir directory established by the kasan initialization code gets eliminated as result. As the kernel runs in DAT-on mode only the PSW_KERNEL_BITS define gets PSW_MASK_DAT bit by default. Additionally, the setup_lowcore_dat_off() and setup_lowcore_dat_on() routines get merged, since there is no DAT-off mode stage anymore. The memory mappings are created with RW+X protection that allows the early boot code setting up all necessary data and services for the kernel being booted. Just before the paging is enabled the memory protection is changed to RO+X for text, RO+NX for read-only data and RW+NX for kernel data and the identity mapping. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/boot/Makefile | 2 +- arch/s390/boot/boot.h | 6 +- arch/s390/boot/startup.c | 45 +++++- arch/s390/boot/vmem.c | 254 ++++++++++++++++++++++++++++++++ arch/s390/include/asm/kasan.h | 4 - arch/s390/include/asm/pgtable.h | 1 + arch/s390/include/asm/ptrace.h | 2 +- arch/s390/include/asm/setup.h | 3 + arch/s390/kernel/early.c | 5 +- arch/s390/kernel/idle.c | 4 +- arch/s390/kernel/process.c | 4 +- arch/s390/kernel/setup.c | 84 +++++------ arch/s390/kernel/smp.c | 4 +- arch/s390/kernel/vmlinux.lds.S | 3 + arch/s390/mm/init.c | 33 +---- arch/s390/mm/kasan_init.c | 85 +---------- arch/s390/mm/vmem.c | 96 +++++++++++- 17 files changed, 447 insertions(+), 188 deletions(-) create mode 100644 arch/s390/boot/vmem.c diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index d52c3e2e16bc..47a397da0498 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -35,7 +35,7 @@ endif CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char -obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o +obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o vmem.o obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o obj-y += version.o pgm_check_info.o ctype.o ipl_data.o machine_kexec_reloc.o obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_PGSTE)) += uv.o diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 286441cf3bf0..547614496e7e 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -16,7 +16,7 @@ struct machine_info { struct vmlinux_info { unsigned long default_lma; - void (*entry)(void); + unsigned long entry; unsigned long image_size; /* does not include .bss */ unsigned long bss_size; /* uncompressed image .bss size */ unsigned long bootdata_off; @@ -27,6 +27,9 @@ struct vmlinux_info { unsigned long rela_dyn_start; unsigned long rela_dyn_end; unsigned long amode31_size; + unsigned long init_mm_off; + unsigned long swapper_pg_dir_off; + unsigned long invalid_pg_dir_off; }; void startup_kernel(void); @@ -41,6 +44,7 @@ void print_missing_facilities(void); void sclp_early_setup_buffer(void); void print_pgm_check_info(void); unsigned long get_random_base(unsigned long safe_addr); +void setup_vmem(unsigned long online_end, unsigned long asce_limit); void __printf(1, 2) decompressor_printk(const char *fmt, ...); void error(char *m); diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index da6ee587fe9a..c5d59df9fa62 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "decompressor.h" #include "boot.h" #include "uv.h" @@ -166,9 +167,10 @@ static void setup_ident_map_size(unsigned long max_physmem_end) #endif } -static void setup_kernel_memory_layout(void) +static unsigned long setup_kernel_memory_layout(void) { unsigned long vmemmap_start; + unsigned long asce_limit; unsigned long rte_size; unsigned long pages; unsigned long vmax; @@ -183,10 +185,10 @@ static void setup_kernel_memory_layout(void) vmalloc_size > _REGION2_SIZE || vmemmap_start + vmemmap_size + vmalloc_size + MODULES_LEN > _REGION2_SIZE) { - vmax = _REGION1_SIZE; + asce_limit = _REGION1_SIZE; rte_size = _REGION2_SIZE; } else { - vmax = _REGION2_SIZE; + asce_limit = _REGION2_SIZE; rte_size = _REGION3_SIZE; } /* @@ -194,7 +196,7 @@ static void setup_kernel_memory_layout(void) * secure storage limit, so that any vmalloc allocation * we do could be used to back secure guest storage. */ - vmax = adjust_to_uv_max(vmax); + vmax = adjust_to_uv_max(asce_limit); #ifdef CONFIG_KASAN /* force vmalloc and modules below kasan shadow */ vmax = min(vmax, KASAN_SHADOW_START); @@ -223,6 +225,8 @@ static void setup_kernel_memory_layout(void) /* make sure vmemmap doesn't overlay with vmalloc area */ VMALLOC_START = max(vmemmap_start + vmemmap_size, VMALLOC_START); vmemmap = (struct page *)vmemmap_start; + + return asce_limit; } /* @@ -256,6 +260,9 @@ static void offset_vmlinux_info(unsigned long offset) vmlinux.rela_dyn_start += offset; vmlinux.rela_dyn_end += offset; vmlinux.dynsym_start += offset; + vmlinux.init_mm_off += offset; + vmlinux.swapper_pg_dir_off += offset; + vmlinux.invalid_pg_dir_off += offset; } static unsigned long reserve_amode31(unsigned long safe_addr) @@ -268,7 +275,10 @@ void startup_kernel(void) { unsigned long random_lma; unsigned long safe_addr; + unsigned long asce_limit; + unsigned long online_end; void *img; + psw_t psw; detect_facilities(); @@ -290,7 +300,8 @@ void startup_kernel(void) sanitize_prot_virt_host(); setup_ident_map_size(detect_memory()); setup_vmalloc_size(); - setup_kernel_memory_layout(); + asce_limit = setup_kernel_memory_layout(); + online_end = min(get_mem_detect_end(), ident_map_size); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) { random_lma = get_random_base(safe_addr); @@ -307,9 +318,23 @@ void startup_kernel(void) } else if (__kaslr_offset) memcpy((void *)vmlinux.default_lma, img, vmlinux.image_size); + /* + * The order of the following operations is important: + * + * - handle_relocs() must follow clear_bss_section() to establish static + * memory references to data in .bss to be used by setup_vmem() + * (i.e init_mm.pgd) + * + * - setup_vmem() must follow handle_relocs() to be able using + * static memory references to data in .bss (i.e init_mm.pgd) + * + * - copy_bootdata() must follow setup_vmem() to propagate changes to + * bootdata made by setup_vmem() + */ clear_bss_section(); - copy_bootdata(); handle_relocs(__kaslr_offset); + setup_vmem(online_end, asce_limit); + copy_bootdata(); if (__kaslr_offset) { /* @@ -321,5 +346,11 @@ void startup_kernel(void) if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) memset(img, 0, vmlinux.image_size); } - vmlinux.entry(); + + /* + * Jump to the decompressed kernel entry point and switch DAT mode on. + */ + psw.addr = vmlinux.entry; + psw.mask = PSW_KERNEL_BITS; + __load_psw(psw); } diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c new file mode 100644 index 000000000000..db1469c17289 --- /dev/null +++ b/arch/s390/boot/vmem.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include "decompressor.h" +#include "boot.h" + +#define init_mm (*(struct mm_struct *)vmlinux.init_mm_off) +#define swapper_pg_dir vmlinux.swapper_pg_dir_off +#define invalid_pg_dir vmlinux.invalid_pg_dir_off + +unsigned long __bootdata_preserved(s390_invalid_asce); +unsigned long __bootdata(pgalloc_pos); +unsigned long __bootdata(pgalloc_end); +unsigned long __bootdata(pgalloc_low); + +static void boot_check_oom(void) +{ + if (pgalloc_pos < pgalloc_low) + error("out of memory on boot\n"); +} + +static void pgtable_populate_begin(unsigned long online_end) +{ + unsigned long initrd_end; + unsigned long kernel_end; + + kernel_end = vmlinux.default_lma + vmlinux.image_size + vmlinux.bss_size; + pgalloc_low = round_up(kernel_end, PAGE_SIZE); + if (IS_ENABLED(CONFIG_BLK_DEV_INITRD)) { + initrd_end = round_up(initrd_data.start + initrd_data.size, _SEGMENT_SIZE); + pgalloc_low = max(pgalloc_low, initrd_end); + } + + pgalloc_end = round_down(online_end, PAGE_SIZE); + pgalloc_pos = pgalloc_end; + + boot_check_oom(); +} + +static void *boot_alloc_pages(unsigned int order) +{ + unsigned long size = PAGE_SIZE << order; + + pgalloc_pos -= size; + pgalloc_pos = round_down(pgalloc_pos, size); + + boot_check_oom(); + + return (void *)pgalloc_pos; +} + +static void *boot_crst_alloc(unsigned long val) +{ + unsigned long *table; + + table = boot_alloc_pages(CRST_ALLOC_ORDER); + if (table) + crst_table_init(table, val); + return table; +} + +static pte_t *boot_pte_alloc(void) +{ + static void *pte_leftover; + pte_t *pte; + + BUILD_BUG_ON(_PAGE_TABLE_SIZE * 2 != PAGE_SIZE); + + if (!pte_leftover) { + pte_leftover = boot_alloc_pages(0); + pte = pte_leftover + _PAGE_TABLE_SIZE; + } else { + pte = pte_leftover; + pte_leftover = NULL; + } + memset64((u64 *)pte, _PAGE_INVALID, PTRS_PER_PTE); + return pte; +} + +static bool can_large_pud(pud_t *pu_dir, unsigned long addr, unsigned long end) +{ + return machine.has_edat2 && + IS_ALIGNED(addr, PUD_SIZE) && (end - addr) >= PUD_SIZE; +} + +static bool can_large_pmd(pmd_t *pm_dir, unsigned long addr, unsigned long end) +{ + return machine.has_edat1 && + IS_ALIGNED(addr, PMD_SIZE) && (end - addr) >= PMD_SIZE; +} + +static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long end) +{ + unsigned long next; + pte_t *pte, entry; + + pte = pte_offset_kernel(pmd, addr); + for (; addr < end; addr += PAGE_SIZE, pte++) { + if (pte_none(*pte)) { + entry = __pte(__pa(addr)); + entry = set_pte_bit(entry, PAGE_KERNEL_EXEC); + set_pte(pte, entry); + } + } +} + +static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long end) +{ + unsigned long next; + pmd_t *pmd, entry; + pte_t *pte; + + pmd = pmd_offset(pud, addr); + for (; addr < end; addr = next, pmd++) { + next = pmd_addr_end(addr, end); + if (pmd_none(*pmd)) { + if (can_large_pmd(pmd, addr, next)) { + entry = __pmd(__pa(addr)); + entry = set_pmd_bit(entry, SEGMENT_KERNEL_EXEC); + set_pmd(pmd, entry); + continue; + } + pte = boot_pte_alloc(); + pmd_populate(&init_mm, pmd, pte); + } else if (pmd_large(*pmd)) { + continue; + } + pgtable_pte_populate(pmd, addr, next); + } +} + +static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long end) +{ + unsigned long next; + pud_t *pud, entry; + pmd_t *pmd; + + pud = pud_offset(p4d, addr); + for (; addr < end; addr = next, pud++) { + next = pud_addr_end(addr, end); + if (pud_none(*pud)) { + if (can_large_pud(pud, addr, next)) { + entry = __pud(__pa(addr)); + entry = set_pud_bit(entry, REGION3_KERNEL_EXEC); + set_pud(pud, entry); + continue; + } + pmd = boot_crst_alloc(_SEGMENT_ENTRY_EMPTY); + pud_populate(&init_mm, pud, pmd); + } else if (pud_large(*pud)) { + continue; + } + pgtable_pmd_populate(pud, addr, next); + } +} + +static void pgtable_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long end) +{ + unsigned long next; + p4d_t *p4d; + pud_t *pud; + + p4d = p4d_offset(pgd, addr); + for (; addr < end; addr = next, p4d++) { + next = p4d_addr_end(addr, end); + if (p4d_none(*p4d)) { + pud = boot_crst_alloc(_REGION3_ENTRY_EMPTY); + p4d_populate(&init_mm, p4d, pud); + } + pgtable_pud_populate(p4d, addr, next); + } +} + +static void pgtable_populate(unsigned long addr, unsigned long end) +{ + unsigned long next; + pgd_t *pgd; + p4d_t *p4d; + + pgd = pgd_offset(&init_mm, addr); + for (; addr < end; addr = next, pgd++) { + next = pgd_addr_end(addr, end); + if (pgd_none(*pgd)) { + p4d = boot_crst_alloc(_REGION2_ENTRY_EMPTY); + pgd_populate(&init_mm, pgd, p4d); + } + pgtable_p4d_populate(pgd, addr, next); + } +} + +/* + * The pgtables are located in the range [pgalloc_pos, pgalloc_end). + * That range must stay intact and is later reserved in the memblock. + * Therefore pgtable_populate(pgalloc_pos, pgalloc_end) is needed to + * finalize pgalloc_pos pointer. However that call can decrease the + * value of pgalloc_pos pointer itself. Therefore, pgtable_populate() + * needs to be called repeatedly until pgtables are complete and + * pgalloc_pos does not grow left anymore. + */ +static void pgtable_populate_end(void) +{ + unsigned long pgalloc_end_curr = pgalloc_end; + unsigned long pgalloc_pos_prev; + + do { + pgalloc_pos_prev = pgalloc_pos; + pgtable_populate(pgalloc_pos, pgalloc_end_curr); + pgalloc_end_curr = pgalloc_pos_prev; + } while (pgalloc_pos < pgalloc_pos_prev); +} + +void setup_vmem(unsigned long online_end, unsigned long asce_limit) +{ + unsigned long asce_type; + unsigned long asce_bits; + + if (asce_limit == _REGION1_SIZE) { + asce_type = _REGION2_ENTRY_EMPTY; + asce_bits = _ASCE_TYPE_REGION2 | _ASCE_TABLE_LENGTH; + } else { + asce_type = _REGION3_ENTRY_EMPTY; + asce_bits = _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH; + } + s390_invalid_asce = invalid_pg_dir | _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH; + + crst_table_init((unsigned long *)swapper_pg_dir, asce_type); + crst_table_init((unsigned long *)invalid_pg_dir, _REGION3_ENTRY_EMPTY); + + /* + * To allow prefixing the lowcore must be mapped with 4KB pages. + * To prevent creation of a large page at address 0 first map + * the lowcore and create the identity mapping only afterwards. + * + * No further pgtable_populate() calls are allowed after the value + * of pgalloc_pos finalized with a call to pgtable_populate_end(). + */ + pgtable_populate_begin(online_end); + pgtable_populate(0, sizeof(struct lowcore)); + pgtable_populate(0, online_end); + pgtable_populate_end(); + + S390_lowcore.kernel_asce = swapper_pg_dir | asce_bits; + S390_lowcore.user_asce = s390_invalid_asce; + + __ctl_load(S390_lowcore.kernel_asce, 1, 1); + __ctl_load(S390_lowcore.user_asce, 7, 7); + __ctl_load(S390_lowcore.kernel_asce, 13, 13); + + init_mm.context.asce = S390_lowcore.kernel_asce; +} diff --git a/arch/s390/include/asm/kasan.h b/arch/s390/include/asm/kasan.h index 2768d5db181f..f7244cc16240 100644 --- a/arch/s390/include/asm/kasan.h +++ b/arch/s390/include/asm/kasan.h @@ -14,8 +14,6 @@ #define KASAN_SHADOW_END (KASAN_SHADOW_START + KASAN_SHADOW_SIZE) extern void kasan_early_init(void); -extern void kasan_copy_shadow_mapping(void); -extern void kasan_free_early_identity(void); /* * Estimate kasan memory requirements, which it will reserve @@ -43,8 +41,6 @@ static inline unsigned long kasan_estimate_memory_needs(unsigned long physmem) } #else static inline void kasan_early_init(void) { } -static inline void kasan_copy_shadow_mapping(void) { } -static inline void kasan_free_early_identity(void) { } static inline unsigned long kasan_estimate_memory_needs(unsigned long physmem) { return 0; } #endif diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 0170f95f3b91..0f1eba005f6d 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -23,6 +23,7 @@ #include extern pgd_t swapper_pg_dir[]; +extern pgd_t invalid_pg_dir[]; extern void paging_init(void); extern unsigned long s390_invalid_asce; diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 8bae33ab320a..bfb8c3cb8aee 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -26,7 +26,7 @@ #ifndef __ASSEMBLY__ #define PSW_KERNEL_BITS (PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_ASC_HOME | \ - PSW_MASK_EA | PSW_MASK_BA) + PSW_MASK_EA | PSW_MASK_BA | PSW_MASK_DAT) #define PSW_USER_BITS (PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | \ PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_MCHECK | \ PSW_MASK_PSTATE | PSW_ASC_PRIMARY) diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 77e6506898f5..6792ce28d37a 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -73,6 +73,9 @@ extern unsigned int zlib_dfltcc_support; extern int noexec_disabled; extern unsigned long ident_map_size; +extern unsigned long pgalloc_pos; +extern unsigned long pgalloc_end; +extern unsigned long pgalloc_low; /* The Write Back bit position in the physaddr is given by the SLPC PCI */ extern unsigned long mio_wb_bit_mask; diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 9693c8630e73..9cfd9f4fc927 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -160,9 +161,7 @@ static noinline __init void setup_lowcore_early(void) psw_t psw; psw.addr = (unsigned long)early_pgm_check_handler; - psw.mask = PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA; - if (IS_ENABLED(CONFIG_KASAN)) - psw.mask |= PSW_MASK_DAT; + psw.mask = PSW_KERNEL_BITS; S390_lowcore.program_new_psw = psw; S390_lowcore.preempt_count = INIT_PREEMPT_COUNT; } diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 4bf1ee293f2b..a8aebb5c95cf 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -51,8 +51,8 @@ void arch_cpu_idle(void) unsigned long psw_mask; /* Wait for external, I/O or machine check interrupt. */ - psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | - PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; + psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | + PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; clear_cpu_flag(CIF_NOHZ_DELAY); /* psw_idle() returns with interrupts disabled. */ diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 3f5d2db0b854..67df64ef4839 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -147,8 +147,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) if (unlikely(args->fn)) { /* kernel thread */ memset(&frame->childregs, 0, sizeof(struct pt_regs)); - frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT | - PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; + frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | + PSW_MASK_EXT | PSW_MASK_MCHECK; frame->childregs.psw.addr = (unsigned long)__ret_from_fork; frame->childregs.gprs[9] = (unsigned long)args->fn; diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 2b6091349daa..1ffaa85cd518 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -149,6 +149,9 @@ int __bootdata(noexec_disabled); unsigned long __bootdata(ident_map_size); struct mem_detect_info __bootdata(mem_detect); struct initrd_data __bootdata(initrd_data); +unsigned long __bootdata(pgalloc_pos); +unsigned long __bootdata(pgalloc_end); +unsigned long __bootdata(pgalloc_low); unsigned long __bootdata_preserved(__kaslr_offset); unsigned long __bootdata(__amode31_base); @@ -411,16 +414,12 @@ void __init arch_call_rest_init(void) call_on_stack_noreturn(rest_init, stack); } -static void __init setup_lowcore_dat_off(void) +static void __init setup_lowcore(void) { - unsigned long int_psw_mask = PSW_KERNEL_BITS; - struct lowcore *abs_lc, *lc; + struct lowcore *lc, *abs_lc; unsigned long mcck_stack; unsigned long flags; - if (IS_ENABLED(CONFIG_KASAN)) - int_psw_mask |= PSW_MASK_DAT; - /* * Setup lowcore for boot cpu */ @@ -430,17 +429,17 @@ static void __init setup_lowcore_dat_off(void) panic("%s: Failed to allocate %zu bytes align=%zx\n", __func__, sizeof(*lc), sizeof(*lc)); - lc->restart_psw.mask = PSW_KERNEL_BITS; - lc->restart_psw.addr = (unsigned long) restart_int_handler; - lc->external_new_psw.mask = int_psw_mask | PSW_MASK_MCHECK; + lc->restart_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_DAT; + lc->restart_psw.addr = __pa(restart_int_handler); + lc->external_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK; lc->external_new_psw.addr = (unsigned long) ext_int_handler; - lc->svc_new_psw.mask = int_psw_mask | PSW_MASK_MCHECK; + lc->svc_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK; lc->svc_new_psw.addr = (unsigned long) system_call; - lc->program_new_psw.mask = int_psw_mask | PSW_MASK_MCHECK; + lc->program_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK; lc->program_new_psw.addr = (unsigned long) pgm_check_handler; - lc->mcck_new_psw.mask = int_psw_mask; + lc->mcck_new_psw.mask = PSW_KERNEL_BITS; lc->mcck_new_psw.addr = (unsigned long) mcck_int_handler; - lc->io_new_psw.mask = int_psw_mask | PSW_MASK_MCHECK; + lc->io_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK; lc->io_new_psw.addr = (unsigned long) io_int_handler; lc->clock_comparator = clock_comparator_max; lc->nodat_stack = ((unsigned long) &init_thread_union) @@ -477,15 +476,7 @@ static void __init setup_lowcore_dat_off(void) lc->restart_fn = (unsigned long) do_restart; lc->restart_data = 0; lc->restart_source = -1U; - - abs_lc = get_abs_lowcore(&flags); - abs_lc->restart_stack = lc->restart_stack; - abs_lc->restart_fn = lc->restart_fn; - abs_lc->restart_data = lc->restart_data; - abs_lc->restart_source = lc->restart_source; - abs_lc->restart_psw = lc->restart_psw; - abs_lc->mcesad = lc->mcesad; - put_abs_lowcore(abs_lc, flags); + __ctl_store(lc->cregs_save_area, 0, 15); mcck_stack = (unsigned long)memblock_alloc(THREAD_SIZE, THREAD_SIZE); if (!mcck_stack) @@ -499,33 +490,26 @@ static void __init setup_lowcore_dat_off(void) lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW); lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW); lc->preempt_count = PREEMPT_DISABLED; + lc->kernel_asce = S390_lowcore.kernel_asce; + lc->user_asce = S390_lowcore.user_asce; + + abs_lc = get_abs_lowcore(&flags); + abs_lc->restart_stack = lc->restart_stack; + abs_lc->restart_fn = lc->restart_fn; + abs_lc->restart_data = lc->restart_data; + abs_lc->restart_source = lc->restart_source; + abs_lc->restart_psw = lc->restart_psw; + abs_lc->restart_flags = RESTART_FLAG_CTLREGS; + memcpy(abs_lc->cregs_save_area, lc->cregs_save_area, sizeof(abs_lc->cregs_save_area)); + abs_lc->program_new_psw = lc->program_new_psw; + abs_lc->mcesad = lc->mcesad; + put_abs_lowcore(abs_lc, flags); set_prefix(__pa(lc)); lowcore_ptr[0] = lc; -} - -static void __init setup_lowcore_dat_on(void) -{ - struct lowcore *abs_lc; - unsigned long flags; - - __ctl_clear_bit(0, 28); - S390_lowcore.external_new_psw.mask |= PSW_MASK_DAT; - S390_lowcore.svc_new_psw.mask |= PSW_MASK_DAT; - S390_lowcore.program_new_psw.mask |= PSW_MASK_DAT; - S390_lowcore.mcck_new_psw.mask |= PSW_MASK_DAT; - S390_lowcore.io_new_psw.mask |= PSW_MASK_DAT; - __ctl_set_bit(0, 28); - __ctl_store(S390_lowcore.cregs_save_area, 0, 15); if (abs_lowcore_map(0, lowcore_ptr[0], true)) panic("Couldn't setup absolute lowcore"); abs_lowcore_mapped = true; - abs_lc = get_abs_lowcore(&flags); - abs_lc->restart_flags = RESTART_FLAG_CTLREGS; - abs_lc->program_new_psw = S390_lowcore.program_new_psw; - memcpy(abs_lc->cregs_save_area, S390_lowcore.cregs_save_area, - sizeof(abs_lc->cregs_save_area)); - put_abs_lowcore(abs_lc, flags); } static struct resource code_resource = { @@ -649,6 +633,14 @@ static struct notifier_block kdump_mem_nb = { #endif +/* + * Reserve page tables created by decompressor + */ +static void __init reserve_pgtables(void) +{ + memblock_reserve(pgalloc_pos, pgalloc_end - pgalloc_pos); +} + /* * Reserve memory for kdump kernel to be loaded with kexec */ @@ -1004,6 +996,7 @@ void __init setup_arch(char **cmdline_p) setup_control_program_code(); /* Do some memory reservations *before* memory is added to memblock */ + reserve_pgtables(); reserve_kernel(); reserve_initrd(); reserve_certificate_list(); @@ -1038,7 +1031,7 @@ void __init setup_arch(char **cmdline_p) #endif setup_resources(); - setup_lowcore_dat_off(); + setup_lowcore(); smp_fill_possible_mask(); cpu_detect_mhz_feature(); cpu_init(); @@ -1050,7 +1043,7 @@ void __init setup_arch(char **cmdline_p) static_branch_enable(&cpu_has_bear); /* - * Create kernel page tables and switch to virtual addressing. + * Create kernel page tables. */ paging_init(); memcpy_real_init(); @@ -1058,7 +1051,6 @@ void __init setup_arch(char **cmdline_p) * After paging_init created the kernel page table, the new PSWs * in lowcore can now run with DAT enabled. */ - setup_lowcore_dat_on(); #ifdef CONFIG_CRASH_DUMP smp_save_dump_ipl_cpu(); #endif diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 0031325ce4bc..24f19f10b237 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -327,7 +327,7 @@ static void pcpu_delegate(struct pcpu *pcpu, lc = lowcore_ptr[pcpu - pcpu_devices]; source_cpu = stap(); - __load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT); + if (pcpu->address == source_cpu) { call_on_stack(2, stack, void, __pcpu_delegate, pcpu_delegate_fn *, func, void *, data); @@ -488,7 +488,7 @@ void smp_send_stop(void) int cpu; /* Disable all interrupts/machine checks */ - __load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT); + __load_psw_mask(PSW_KERNEL_BITS); trace_hardirqs_off(); debug_set_critical(); diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 5ea3830af0cc..a965ddc34f43 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -213,6 +213,9 @@ SECTIONS QUAD(__rela_dyn_start) /* rela_dyn_start */ QUAD(__rela_dyn_end) /* rela_dyn_end */ QUAD(_eamode31 - _samode31) /* amode31_size */ + QUAD(init_mm) + QUAD(swapper_pg_dir) + QUAD(invalid_pg_dir) } :NONE /* Debugging sections. */ diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 30ab55f868f6..144447d5cb4c 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -52,9 +52,9 @@ #include pgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".bss..swapper_pg_dir"); -static pgd_t invalid_pg_dir[PTRS_PER_PGD] __section(".bss..invalid_pg_dir"); +pgd_t invalid_pg_dir[PTRS_PER_PGD] __section(".bss..invalid_pg_dir"); -unsigned long s390_invalid_asce; +unsigned long __bootdata_preserved(s390_invalid_asce); unsigned long empty_zero_page, zero_page_mask; EXPORT_SYMBOL(empty_zero_page); @@ -93,37 +93,8 @@ static void __init setup_zero_pages(void) void __init paging_init(void) { unsigned long max_zone_pfns[MAX_NR_ZONES]; - unsigned long pgd_type, asce_bits; - psw_t psw; - s390_invalid_asce = (unsigned long)invalid_pg_dir; - s390_invalid_asce |= _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH; - crst_table_init((unsigned long *)invalid_pg_dir, _REGION3_ENTRY_EMPTY); - init_mm.pgd = swapper_pg_dir; - if (VMALLOC_END > _REGION2_SIZE) { - asce_bits = _ASCE_TYPE_REGION2 | _ASCE_TABLE_LENGTH; - pgd_type = _REGION2_ENTRY_EMPTY; - } else { - asce_bits = _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH; - pgd_type = _REGION3_ENTRY_EMPTY; - } - init_mm.context.asce = (__pa(init_mm.pgd) & PAGE_MASK) | asce_bits; - S390_lowcore.kernel_asce = init_mm.context.asce; - S390_lowcore.user_asce = s390_invalid_asce; - crst_table_init((unsigned long *) init_mm.pgd, pgd_type); vmem_map_init(); - kasan_copy_shadow_mapping(); - - /* enable virtual mapping in kernel mode */ - __ctl_load(S390_lowcore.kernel_asce, 1, 1); - __ctl_load(S390_lowcore.user_asce, 7, 7); - __ctl_load(S390_lowcore.kernel_asce, 13, 13); - psw.mask = __extract_psw(); - psw_bits(psw).dat = 1; - psw_bits(psw).as = PSW_BITS_AS_HOME; - __load_psw_mask(psw.mask); - kasan_free_early_identity(); - sparse_init(); zone_dma_bits = 31; memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index a97b7981358e..801d81c189a7 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include #include -#include #include #include #include @@ -15,16 +14,11 @@ static unsigned long segment_pos __initdata; static unsigned long segment_low __initdata; -static unsigned long pgalloc_pos __initdata; -static unsigned long pgalloc_low __initdata; -static unsigned long pgalloc_freeable __initdata; static bool has_edat __initdata; static bool has_nx __initdata; #define __sha(x) ((unsigned long)kasan_mem_to_shadow((void *)x)) -static pgd_t early_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); - static void __init kasan_early_panic(const char *reason) { sclp_early_printk("The Linux kernel failed to boot with the KernelAddressSanitizer:\n"); @@ -229,29 +223,6 @@ static void __init kasan_early_pgtable_populate(unsigned long address, } } -static void __init kasan_set_pgd(pgd_t *pgd, unsigned long asce_type) -{ - unsigned long asce_bits; - - asce_bits = asce_type | _ASCE_TABLE_LENGTH; - S390_lowcore.kernel_asce = (__pa(pgd) & PAGE_MASK) | asce_bits; - S390_lowcore.user_asce = S390_lowcore.kernel_asce; - - __ctl_load(S390_lowcore.kernel_asce, 1, 1); - __ctl_load(S390_lowcore.kernel_asce, 7, 7); - __ctl_load(S390_lowcore.kernel_asce, 13, 13); -} - -static void __init kasan_enable_dat(void) -{ - psw_t psw; - - psw.mask = __extract_psw(); - psw_bits(psw).dat = 1; - psw_bits(psw).as = PSW_BITS_AS_HOME; - __load_psw_mask(psw.mask); -} - static void __init kasan_early_detect_facilities(void) { if (test_facility(8)) { @@ -272,7 +243,6 @@ void __init kasan_early_init(void) p4d_t p4d_z = __p4d(__pa(kasan_early_shadow_pud) | _REGION2_ENTRY); unsigned long untracked_end = MODULES_VADDR; unsigned long shadow_alloc_size; - unsigned long initrd_end; unsigned long memsize; kasan_early_detect_facilities(); @@ -298,36 +268,24 @@ void __init kasan_early_init(void) BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, P4D_SIZE)); BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, P4D_SIZE)); - crst_table_init((unsigned long *)early_pg_dir, _REGION2_ENTRY_EMPTY); /* init kasan zero shadow */ - crst_table_init((unsigned long *)kasan_early_shadow_p4d, - p4d_val(p4d_z)); - crst_table_init((unsigned long *)kasan_early_shadow_pud, - pud_val(pud_z)); - crst_table_init((unsigned long *)kasan_early_shadow_pmd, - pmd_val(pmd_z)); + crst_table_init((unsigned long *)kasan_early_shadow_p4d, p4d_val(p4d_z)); + crst_table_init((unsigned long *)kasan_early_shadow_pud, pud_val(pud_z)); + crst_table_init((unsigned long *)kasan_early_shadow_pmd, pmd_val(pmd_z)); memset64((u64 *)kasan_early_shadow_pte, pte_val(pte_z), PTRS_PER_PTE); shadow_alloc_size = memsize >> KASAN_SHADOW_SCALE_SHIFT; - pgalloc_low = round_up((unsigned long)_end, _SEGMENT_SIZE); - if (IS_ENABLED(CONFIG_BLK_DEV_INITRD)) { - initrd_end = - round_up(initrd_data.start + initrd_data.size, _SEGMENT_SIZE); - pgalloc_low = max(pgalloc_low, initrd_end); - } if (pgalloc_low + shadow_alloc_size > memsize) kasan_early_panic("out of memory during initialisation\n"); if (has_edat) { - segment_pos = round_down(memsize, _SEGMENT_SIZE); + segment_pos = round_down(pgalloc_pos, _SEGMENT_SIZE); segment_low = segment_pos - shadow_alloc_size; + segment_low = round_down(segment_low, _SEGMENT_SIZE); pgalloc_pos = segment_low; - } else { - pgalloc_pos = memsize; } - init_mm.pgd = early_pg_dir; /* * Current memory layout: * +- 0 -------------+ +- shadow start -+ @@ -376,40 +334,7 @@ void __init kasan_early_init(void) POPULATE_ZERO_SHADOW); kasan_early_pgtable_populate(__sha(MODULES_END), __sha(_REGION1_SIZE), POPULATE_ZERO_SHADOW); - /* memory allocated for identity mapping structs will be freed later */ - pgalloc_freeable = pgalloc_pos; - /* populate identity mapping */ - kasan_early_pgtable_populate(0, memsize, POPULATE_ONE2ONE); - kasan_set_pgd(early_pg_dir, _ASCE_TYPE_REGION2); - kasan_enable_dat(); /* enable kasan */ init_task.kasan_depth = 0; - memblock_reserve(pgalloc_pos, memsize - pgalloc_pos); sclp_early_printk("KernelAddressSanitizer initialized\n"); } - -void __init kasan_copy_shadow_mapping(void) -{ - /* - * At this point we are still running on early pages setup early_pg_dir, - * while swapper_pg_dir has just been initialized with identity mapping. - * Carry over shadow memory region from early_pg_dir to swapper_pg_dir. - */ - - pgd_t *pg_dir_src; - pgd_t *pg_dir_dst; - p4d_t *p4_dir_src; - p4d_t *p4_dir_dst; - - pg_dir_src = pgd_offset_raw(early_pg_dir, KASAN_SHADOW_START); - pg_dir_dst = pgd_offset_raw(init_mm.pgd, KASAN_SHADOW_START); - p4_dir_src = p4d_offset(pg_dir_src, KASAN_SHADOW_START); - p4_dir_dst = p4d_offset(pg_dir_dst, KASAN_SHADOW_START); - memcpy(p4_dir_dst, p4_dir_src, - (KASAN_SHADOW_SIZE >> P4D_SHIFT) * sizeof(p4d_t)); -} - -void __init kasan_free_early_identity(void) -{ - memblock_phys_free(pgalloc_pos, pgalloc_freeable - pgalloc_pos); -} diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index ee1a97078527..78d7768f93d7 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -657,6 +658,29 @@ void vmem_unmap_4k_page(unsigned long addr) mutex_unlock(&vmem_mutex); } +static int __init memblock_region_cmp(const void *a, const void *b) +{ + const struct memblock_region *r1 = a; + const struct memblock_region *r2 = b; + + if (r1->base < r2->base) + return -1; + if (r1->base > r2->base) + return 1; + return 0; +} + +static void __init memblock_region_swap(void *a, void *b, int size) +{ + struct memblock_region *r1 = a; + struct memblock_region *r2 = b; + struct memblock_region swap; + + swap = *r1; + *r1 = *r2; + *r2 = swap; +} + /* * map whole physical memory to virtual memory (identity mapping) * we reserve enough space in the vmalloc area for vmemmap to hotplug @@ -664,11 +688,68 @@ void vmem_unmap_4k_page(unsigned long addr) */ void __init vmem_map_init(void) { + struct memblock_region memory_rwx_regions[] = { + { + .base = 0, + .size = sizeof(struct lowcore), + .flags = MEMBLOCK_NONE, +#ifdef CONFIG_NUMA + .nid = NUMA_NO_NODE, +#endif + }, + { + .base = __pa(_stext), + .size = _etext - _stext, + .flags = MEMBLOCK_NONE, +#ifdef CONFIG_NUMA + .nid = NUMA_NO_NODE, +#endif + }, + { + .base = __pa(_sinittext), + .size = _einittext - _sinittext, + .flags = MEMBLOCK_NONE, +#ifdef CONFIG_NUMA + .nid = NUMA_NO_NODE, +#endif + }, + { + .base = __stext_amode31, + .size = __etext_amode31 - __stext_amode31, + .flags = MEMBLOCK_NONE, +#ifdef CONFIG_NUMA + .nid = NUMA_NO_NODE, +#endif + }, + }; + struct memblock_type memory_rwx = { + .regions = memory_rwx_regions, + .cnt = ARRAY_SIZE(memory_rwx_regions), + .max = ARRAY_SIZE(memory_rwx_regions), + }; phys_addr_t base, end; u64 i; - for_each_mem_range(i, &base, &end) - vmem_add_range(base, end - base); + /* + * Set RW+NX attribute on all memory, except regions enumerated with + * memory_rwx exclude type. These regions need different attributes, + * which are enforced afterwards. + * + * __for_each_mem_range() iterate and exclude types should be sorted. + * The relative location of _stext and _sinittext is hardcoded in the + * linker script. However a location of __stext_amode31 and the kernel + * image itself are chosen dynamically. Thus, sort the exclude type. + */ + sort(&memory_rwx_regions, + ARRAY_SIZE(memory_rwx_regions), sizeof(memory_rwx_regions[0]), + memblock_region_cmp, memblock_region_swap); + __for_each_mem_range(i, &memblock.memory, &memory_rwx, + NUMA_NO_NODE, MEMBLOCK_NONE, &base, &end, NULL) { + __set_memory((unsigned long)__va(base), + (end - base) >> PAGE_SHIFT, + SET_MEMORY_RW | SET_MEMORY_NX); + } + __set_memory((unsigned long)_stext, (unsigned long)(_etext - _stext) >> PAGE_SHIFT, SET_MEMORY_RO | SET_MEMORY_X); @@ -678,15 +759,14 @@ void __init vmem_map_init(void) __set_memory((unsigned long)_sinittext, (unsigned long)(_einittext - _sinittext) >> PAGE_SHIFT, SET_MEMORY_RO | SET_MEMORY_X); - __set_memory(__stext_amode31, (__etext_amode31 - __stext_amode31) >> PAGE_SHIFT, + __set_memory(__stext_amode31, + (__etext_amode31 - __stext_amode31) >> PAGE_SHIFT, SET_MEMORY_RO | SET_MEMORY_X); - /* lowcore requires 4k mapping for real addresses / prefixing */ - set_memory_4k(0, LC_PAGES); - /* lowcore must be executable for LPSWE */ - if (!static_key_enabled(&cpu_has_bear)) - set_memory_x(0, 1); + if (static_key_enabled(&cpu_has_bear)) + set_memory_nx(0, 1); + set_memory_nx(PAGE_SIZE, 1); pr_info("Write protected kernel read-only data: %luk\n", (unsigned long)(__end_rodata - _stext) >> 10); From 12cf6473d23885fe06aa7e7ca58e990fa4f0737c Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 2 Dec 2022 19:23:11 +0100 Subject: [PATCH 095/182] s390/maccess: remove dead DAT-off code As the kernel is executed in DAT-on mode only, remove unnecessary DAT bit check together with the dead code. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/maccess.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index 4824d1cd33d8..a6314e259f97 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -68,15 +68,11 @@ notrace void *s390_kernel_write(void *dst, const void *src, size_t size) long copied; spin_lock_irqsave(&s390_kernel_write_lock, flags); - if (!(flags & PSW_MASK_DAT)) { - memcpy(dst, src, size); - } else { - while (size) { - copied = s390_kernel_write_odd(tmp, src, size); - tmp += copied; - src += copied; - size -= copied; - } + while (size) { + copied = s390_kernel_write_odd(tmp, src, size); + tmp += copied; + src += copied; + size -= copied; } spin_unlock_irqrestore(&s390_kernel_write_lock, flags); From 07493a9ca79f8a39cfddd0a20b4e6eded4de8f3d Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 16 Dec 2022 19:49:23 +0100 Subject: [PATCH 096/182] s390/kasan: remove identity mapping support The identity mapping is created in the decompressor, there is no need to have the same functionality in the kasan setup code. Thus, remove it. Remove the 4KB pages check for first 1MB since there is no need to take care of the lowcore pages. Acked-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/mm/kasan_init.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 801d81c189a7..bdfbe0dcb7c6 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -75,7 +75,6 @@ static pte_t * __init kasan_early_pte_alloc(void) } enum populate_mode { - POPULATE_ONE2ONE, POPULATE_MAP, POPULATE_ZERO_SHADOW, POPULATE_SHALLOW @@ -101,16 +100,12 @@ static void __init kasan_early_pgtable_populate(unsigned long address, pmd_t pmd; pte_t pte; - if (!has_nx) + if (!has_nx) { pgt_prot_zero = pgprot_clear_bit(pgt_prot_zero, _PAGE_NOEXEC); - if (!has_nx || mode == POPULATE_ONE2ONE) { pgt_prot = pgprot_clear_bit(pgt_prot, _PAGE_NOEXEC); sgt_prot = pgprot_clear_bit(sgt_prot, _SEGMENT_ENTRY_NOEXEC); } - /* - * The first 1MB of 1:1 mapping is mapped with 4KB pages - */ while (address < end) { pg_dir = pgd_offset_k(address); if (pgd_none(*pg_dir)) { @@ -167,15 +162,10 @@ static void __init kasan_early_pgtable_populate(unsigned long address, pmd_populate(&init_mm, pm_dir, kasan_early_shadow_pte); address = (address + PMD_SIZE) & PMD_MASK; continue; - } else if (has_edat && address) { - void *page; + } else if (has_edat) { + void *page = kasan_early_alloc_segment(); - if (mode == POPULATE_ONE2ONE) { - page = (void *)address; - } else { - page = kasan_early_alloc_segment(); - memset(page, 0, _SEGMENT_SIZE); - } + memset(page, 0, _SEGMENT_SIZE); pmd = __pmd(__pa(page)); pmd = set_pmd_bit(pmd, sgt_prot); set_pmd(pm_dir, pmd); @@ -195,12 +185,6 @@ static void __init kasan_early_pgtable_populate(unsigned long address, void *page; switch (mode) { - case POPULATE_ONE2ONE: - page = (void *)address; - pte = __pte(__pa(page)); - pte = set_pte_bit(pte, pgt_prot); - set_pte(pt_dir, pte); - break; case POPULATE_MAP: page = kasan_early_alloc_pages(0); memset(page, 0, PAGE_SIZE); @@ -259,7 +243,6 @@ void __init kasan_early_init(void) * - ident_map_size represents online + standby and memory limits * accounted. * Kasan maps "memsize" right away. - * [0, memsize] - as identity mapping * [__sha(0), __sha(memsize)] - shadow memory for identity mapping * The rest [memsize, ident_map_size] if memsize < ident_map_size * could be mapped/unmapped dynamically later during memory hotplug. From e0e0a87b4b85ac3bbf76327fc030e6134b657068 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 15 Dec 2022 10:33:52 +0100 Subject: [PATCH 097/182] s390/boot: allow setup of different virtual address types Currently the decompressor sets up only identity mapping. Allow adding more address range types as a prerequisite for allocation of kernel fixed mappings. Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/boot/vmem.c | 48 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index db1469c17289..af9124c8c19c 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -17,6 +17,10 @@ unsigned long __bootdata(pgalloc_pos); unsigned long __bootdata(pgalloc_end); unsigned long __bootdata(pgalloc_low); +enum populate_mode { + POPULATE_ONE2ONE, +}; + static void boot_check_oom(void) { if (pgalloc_pos < pgalloc_low) @@ -81,6 +85,16 @@ static pte_t *boot_pte_alloc(void) return pte; } +static unsigned long _pa(unsigned long addr, enum populate_mode mode) +{ + switch (mode) { + case POPULATE_ONE2ONE: + return addr; + default: + return -1; + } +} + static bool can_large_pud(pud_t *pu_dir, unsigned long addr, unsigned long end) { return machine.has_edat2 && @@ -93,7 +107,8 @@ static bool can_large_pmd(pmd_t *pm_dir, unsigned long addr, unsigned long end) IS_ALIGNED(addr, PMD_SIZE) && (end - addr) >= PMD_SIZE; } -static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long end) +static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long end, + enum populate_mode mode) { unsigned long next; pte_t *pte, entry; @@ -101,14 +116,15 @@ static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long e pte = pte_offset_kernel(pmd, addr); for (; addr < end; addr += PAGE_SIZE, pte++) { if (pte_none(*pte)) { - entry = __pte(__pa(addr)); + entry = __pte(_pa(addr, mode)); entry = set_pte_bit(entry, PAGE_KERNEL_EXEC); set_pte(pte, entry); } } } -static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long end) +static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long end, + enum populate_mode mode) { unsigned long next; pmd_t *pmd, entry; @@ -119,7 +135,7 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e next = pmd_addr_end(addr, end); if (pmd_none(*pmd)) { if (can_large_pmd(pmd, addr, next)) { - entry = __pmd(__pa(addr)); + entry = __pmd(_pa(addr, mode)); entry = set_pmd_bit(entry, SEGMENT_KERNEL_EXEC); set_pmd(pmd, entry); continue; @@ -129,11 +145,12 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e } else if (pmd_large(*pmd)) { continue; } - pgtable_pte_populate(pmd, addr, next); + pgtable_pte_populate(pmd, addr, next, mode); } } -static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long end) +static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long end, + enum populate_mode mode) { unsigned long next; pud_t *pud, entry; @@ -144,7 +161,7 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e next = pud_addr_end(addr, end); if (pud_none(*pud)) { if (can_large_pud(pud, addr, next)) { - entry = __pud(__pa(addr)); + entry = __pud(_pa(addr, mode)); entry = set_pud_bit(entry, REGION3_KERNEL_EXEC); set_pud(pud, entry); continue; @@ -154,11 +171,12 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e } else if (pud_large(*pud)) { continue; } - pgtable_pmd_populate(pud, addr, next); + pgtable_pmd_populate(pud, addr, next, mode); } } -static void pgtable_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long end) +static void pgtable_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long end, + enum populate_mode mode) { unsigned long next; p4d_t *p4d; @@ -171,11 +189,11 @@ static void pgtable_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long e pud = boot_crst_alloc(_REGION3_ENTRY_EMPTY); p4d_populate(&init_mm, p4d, pud); } - pgtable_pud_populate(p4d, addr, next); + pgtable_pud_populate(p4d, addr, next, mode); } } -static void pgtable_populate(unsigned long addr, unsigned long end) +static void pgtable_populate(unsigned long addr, unsigned long end, enum populate_mode mode) { unsigned long next; pgd_t *pgd; @@ -188,7 +206,7 @@ static void pgtable_populate(unsigned long addr, unsigned long end) p4d = boot_crst_alloc(_REGION2_ENTRY_EMPTY); pgd_populate(&init_mm, pgd, p4d); } - pgtable_p4d_populate(pgd, addr, next); + pgtable_p4d_populate(pgd, addr, next, mode); } } @@ -208,7 +226,7 @@ static void pgtable_populate_end(void) do { pgalloc_pos_prev = pgalloc_pos; - pgtable_populate(pgalloc_pos, pgalloc_end_curr); + pgtable_populate(pgalloc_pos, pgalloc_end_curr, POPULATE_ONE2ONE); pgalloc_end_curr = pgalloc_pos_prev; } while (pgalloc_pos < pgalloc_pos_prev); } @@ -239,8 +257,8 @@ void setup_vmem(unsigned long online_end, unsigned long asce_limit) * of pgalloc_pos finalized with a call to pgtable_populate_end(). */ pgtable_populate_begin(online_end); - pgtable_populate(0, sizeof(struct lowcore)); - pgtable_populate(0, online_end); + pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); + pgtable_populate(0, online_end, POPULATE_ONE2ONE); pgtable_populate_end(); S390_lowcore.kernel_asce = swapper_pg_dir | asce_bits; From 8e9205d2a58989aff46000ef47021633146ca493 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Sun, 11 Dec 2022 08:18:57 +0100 Subject: [PATCH 098/182] s390/mm: allocate Real Memory Copy Area in decompressor Move Real Memory Copy Area allocation to the decompressor. As result, memcpy_real() and memcpy_real_iter() movers become usable since the very moment the kernel starts. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/boot/startup.c | 2 ++ arch/s390/boot/vmem.c | 15 +++++++++++++++ arch/s390/include/asm/maccess.h | 2 +- arch/s390/kernel/setup.c | 2 +- arch/s390/mm/maccess.c | 9 +-------- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index c5d59df9fa62..cb4b743a5e17 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ unsigned long __bootdata_preserved(__kaslr_offset); unsigned long __bootdata_preserved(__abs_lowcore); unsigned long __bootdata_preserved(__memcpy_real_area); +pte_t *__bootdata_preserved(memcpy_real_ptep); unsigned long __bootdata(__amode31_base); unsigned long __bootdata_preserved(VMALLOC_START); unsigned long __bootdata_preserved(VMALLOC_END); diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index af9124c8c19c..41ff38a0e2dd 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "decompressor.h" #include "boot.h" @@ -12,12 +13,21 @@ #define swapper_pg_dir vmlinux.swapper_pg_dir_off #define invalid_pg_dir vmlinux.invalid_pg_dir_off +/* + * Mimic virt_to_kpte() in lack of init_mm symbol. Skip pmd NULL check though. + */ +static inline pte_t *__virt_to_kpte(unsigned long va) +{ + return pte_offset_kernel(pmd_offset(pud_offset(p4d_offset(pgd_offset_k(va), va), va), va), va); +} + unsigned long __bootdata_preserved(s390_invalid_asce); unsigned long __bootdata(pgalloc_pos); unsigned long __bootdata(pgalloc_end); unsigned long __bootdata(pgalloc_low); enum populate_mode { + POPULATE_NONE, POPULATE_ONE2ONE, }; @@ -88,6 +98,8 @@ static pte_t *boot_pte_alloc(void) static unsigned long _pa(unsigned long addr, enum populate_mode mode) { switch (mode) { + case POPULATE_NONE: + return -1; case POPULATE_ONE2ONE: return addr; default: @@ -259,6 +271,9 @@ void setup_vmem(unsigned long online_end, unsigned long asce_limit) pgtable_populate_begin(online_end); pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); pgtable_populate(0, online_end, POPULATE_ONE2ONE); + pgtable_populate(__memcpy_real_area, __memcpy_real_area + PAGE_SIZE, + POPULATE_NONE); + memcpy_real_ptep = __virt_to_kpte(__memcpy_real_area); pgtable_populate_end(); S390_lowcore.kernel_asce = swapper_pg_dir | asce_bits; diff --git a/arch/s390/include/asm/maccess.h b/arch/s390/include/asm/maccess.h index c7fa838cf6b9..cfec3141fdba 100644 --- a/arch/s390/include/asm/maccess.h +++ b/arch/s390/include/asm/maccess.h @@ -7,7 +7,7 @@ struct iov_iter; extern unsigned long __memcpy_real_area; -void memcpy_real_init(void); +extern pte_t *memcpy_real_ptep; size_t memcpy_real_iter(struct iov_iter *iter, unsigned long src, size_t count); int memcpy_real(void *dest, unsigned long src, size_t count); #ifdef CONFIG_CRASH_DUMP diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 1ffaa85cd518..9ae2f6b3042e 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -1046,7 +1046,7 @@ void __init setup_arch(char **cmdline_p) * Create kernel page tables. */ paging_init(); - memcpy_real_init(); + /* * After paging_init created the kernel page table, the new PSWs * in lowcore can now run with DAT enabled. diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index a6314e259f97..7c66b3ad05e0 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -21,7 +21,7 @@ #include unsigned long __bootdata_preserved(__memcpy_real_area); -static __ro_after_init pte_t *memcpy_real_ptep; +pte_t *__bootdata_preserved(memcpy_real_ptep); static DEFINE_MUTEX(memcpy_real_mutex); static notrace long s390_kernel_write_odd(void *dst, const void *src, size_t size) @@ -79,13 +79,6 @@ notrace void *s390_kernel_write(void *dst, const void *src, size_t size) return dst; } -void __init memcpy_real_init(void) -{ - memcpy_real_ptep = vmem_get_alloc_pte(__memcpy_real_area, true); - if (!memcpy_real_ptep) - panic("Couldn't setup memcpy real area"); -} - size_t memcpy_real_iter(struct iov_iter *iter, unsigned long src, size_t count) { size_t len, copied, res = 0; From 2154e0b3282d0029ea7790a8414d61d5dc7d72ff Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 19 Dec 2022 21:08:27 +0100 Subject: [PATCH 099/182] s390/mm: allocate Absolute Lowcore Area in decompressor Move Absolute Lowcore Area allocation to the decompressor. As result, get_abs_lowcore() and put_abs_lowcore() access brackets become really straight and do not require complex execution context analysis and LAP and interrupts tackling. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 2 ++ arch/s390/boot/vmem.c | 6 ++++ arch/s390/include/asm/abs_lowcore.h | 16 ++++++++-- arch/s390/kernel/abs_lowcore.c | 49 ----------------------------- arch/s390/kernel/ipl.c | 5 ++- arch/s390/kernel/machine_kexec.c | 5 ++- arch/s390/kernel/os_info.c | 5 ++- arch/s390/kernel/setup.c | 8 ++--- arch/s390/kernel/smp.c | 10 +++--- arch/s390/mm/maccess.c | 5 ++- 10 files changed, 36 insertions(+), 75 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 547614496e7e..8abedae76e53 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -66,5 +66,7 @@ extern unsigned char _compressed_end[]; extern struct vmlinux_info _vmlinux_info; #define vmlinux _vmlinux_info +#define __abs_lowcore_pa(x) (((unsigned long)(x) - __abs_lowcore) % sizeof(struct lowcore)) + #endif /* __ASSEMBLY__ */ #endif /* BOOT_BOOT_H */ diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index 41ff38a0e2dd..3bcef4fcea80 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "decompressor.h" #include "boot.h" @@ -29,6 +30,7 @@ unsigned long __bootdata(pgalloc_low); enum populate_mode { POPULATE_NONE, POPULATE_ONE2ONE, + POPULATE_ABS_LOWCORE, }; static void boot_check_oom(void) @@ -102,6 +104,8 @@ static unsigned long _pa(unsigned long addr, enum populate_mode mode) return -1; case POPULATE_ONE2ONE: return addr; + case POPULATE_ABS_LOWCORE: + return __abs_lowcore_pa(addr); default: return -1; } @@ -271,6 +275,8 @@ void setup_vmem(unsigned long online_end, unsigned long asce_limit) pgtable_populate_begin(online_end); pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); pgtable_populate(0, online_end, POPULATE_ONE2ONE); + pgtable_populate(__abs_lowcore, __abs_lowcore + sizeof(struct lowcore), + POPULATE_ABS_LOWCORE); pgtable_populate(__memcpy_real_area, __memcpy_real_area + PAGE_SIZE, POPULATE_NONE); memcpy_real_ptep = __virt_to_kpte(__memcpy_real_area); diff --git a/arch/s390/include/asm/abs_lowcore.h b/arch/s390/include/asm/abs_lowcore.h index 4c61b14ee928..6f264b79e377 100644 --- a/arch/s390/include/asm/abs_lowcore.h +++ b/arch/s390/include/asm/abs_lowcore.h @@ -7,11 +7,21 @@ #define ABS_LOWCORE_MAP_SIZE (NR_CPUS * sizeof(struct lowcore)) extern unsigned long __abs_lowcore; -extern bool abs_lowcore_mapped; -struct lowcore *get_abs_lowcore(unsigned long *flags); -void put_abs_lowcore(struct lowcore *lc, unsigned long flags); int abs_lowcore_map(int cpu, struct lowcore *lc, bool alloc); void abs_lowcore_unmap(int cpu); +static inline struct lowcore *get_abs_lowcore(void) +{ + int cpu; + + cpu = get_cpu(); + return ((struct lowcore *)__abs_lowcore) + cpu; +} + +static inline void put_abs_lowcore(struct lowcore *lc) +{ + put_cpu(); +} + #endif /* _ASM_S390_ABS_LOWCORE_H */ diff --git a/arch/s390/kernel/abs_lowcore.c b/arch/s390/kernel/abs_lowcore.c index fb92e8ed0525..f9efc54ec4b7 100644 --- a/arch/s390/kernel/abs_lowcore.c +++ b/arch/s390/kernel/abs_lowcore.c @@ -3,12 +3,7 @@ #include #include -#define ABS_LOWCORE_UNMAPPED 1 -#define ABS_LOWCORE_LAP_ON 2 -#define ABS_LOWCORE_IRQS_ON 4 - unsigned long __bootdata_preserved(__abs_lowcore); -bool __ro_after_init abs_lowcore_mapped; int abs_lowcore_map(int cpu, struct lowcore *lc, bool alloc) { @@ -49,47 +44,3 @@ void abs_lowcore_unmap(int cpu) addr += PAGE_SIZE; } } - -struct lowcore *get_abs_lowcore(unsigned long *flags) -{ - unsigned long irq_flags; - union ctlreg0 cr0; - int cpu; - - *flags = 0; - cpu = get_cpu(); - if (abs_lowcore_mapped) { - return ((struct lowcore *)__abs_lowcore) + cpu; - } else { - if (cpu != 0) - panic("Invalid unmapped absolute lowcore access\n"); - local_irq_save(irq_flags); - if (!irqs_disabled_flags(irq_flags)) - *flags |= ABS_LOWCORE_IRQS_ON; - __ctl_store(cr0.val, 0, 0); - if (cr0.lap) { - *flags |= ABS_LOWCORE_LAP_ON; - __ctl_clear_bit(0, 28); - } - *flags |= ABS_LOWCORE_UNMAPPED; - return lowcore_ptr[0]; - } -} - -void put_abs_lowcore(struct lowcore *lc, unsigned long flags) -{ - if (abs_lowcore_mapped) { - if (flags) - panic("Invalid mapped absolute lowcore release\n"); - } else { - if (smp_processor_id() != 0) - panic("Invalid mapped absolute lowcore access\n"); - if (!(flags & ABS_LOWCORE_UNMAPPED)) - panic("Invalid unmapped absolute lowcore release\n"); - if (flags & ABS_LOWCORE_LAP_ON) - __ctl_set_bit(0, 28); - if (flags & ABS_LOWCORE_IRQS_ON) - local_irq_enable(); - } - put_cpu(); -} diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index fbd646dbf440..b74b728c29f8 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1986,15 +1986,14 @@ static void dump_reipl_run(struct shutdown_trigger *trigger) { unsigned long ipib = (unsigned long) reipl_block_actual; struct lowcore *abs_lc; - unsigned long flags; unsigned int csum; csum = (__force unsigned int) csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0); - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); abs_lc->ipib = ipib; abs_lc->ipib_checksum = csum; - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); dump_run(trigger); } diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 4579b42286d5..2a8e73266428 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -224,7 +224,6 @@ void machine_kexec_cleanup(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { struct lowcore *abs_lc; - unsigned long flags; VMCOREINFO_SYMBOL(lowcore_ptr); VMCOREINFO_SYMBOL(high_memory); @@ -232,9 +231,9 @@ void arch_crash_save_vmcoreinfo(void) vmcoreinfo_append_str("SAMODE31=%lx\n", __samode31); vmcoreinfo_append_str("EAMODE31=%lx\n", __eamode31); vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset()); - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); abs_lc->vmcore_info = paddr_vmcoreinfo_note(); - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); } void machine_shutdown(void) diff --git a/arch/s390/kernel/os_info.c b/arch/s390/kernel/os_info.c index ec0bd9457e90..6e1824141b29 100644 --- a/arch/s390/kernel/os_info.c +++ b/arch/s390/kernel/os_info.c @@ -59,15 +59,14 @@ void os_info_entry_add(int nr, void *ptr, u64 size) void __init os_info_init(void) { struct lowcore *abs_lc; - unsigned long flags; os_info.version_major = OS_INFO_VERSION_MAJOR; os_info.version_minor = OS_INFO_VERSION_MINOR; os_info.magic = OS_INFO_MAGIC; os_info.csum = os_info_csum(&os_info); - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); abs_lc->os_info = __pa(&os_info); - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); } #ifdef CONFIG_CRASH_DUMP diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 9ae2f6b3042e..7fad2c02a7c3 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -418,7 +418,6 @@ static void __init setup_lowcore(void) { struct lowcore *lc, *abs_lc; unsigned long mcck_stack; - unsigned long flags; /* * Setup lowcore for boot cpu @@ -493,7 +492,7 @@ static void __init setup_lowcore(void) lc->kernel_asce = S390_lowcore.kernel_asce; lc->user_asce = S390_lowcore.user_asce; - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); abs_lc->restart_stack = lc->restart_stack; abs_lc->restart_fn = lc->restart_fn; abs_lc->restart_data = lc->restart_data; @@ -503,13 +502,12 @@ static void __init setup_lowcore(void) memcpy(abs_lc->cregs_save_area, lc->cregs_save_area, sizeof(abs_lc->cregs_save_area)); abs_lc->program_new_psw = lc->program_new_psw; abs_lc->mcesad = lc->mcesad; - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); set_prefix(__pa(lc)); lowcore_ptr[0] = lc; - if (abs_lowcore_map(0, lowcore_ptr[0], true)) + if (abs_lowcore_map(0, lowcore_ptr[0], false)) panic("Couldn't setup absolute lowcore"); - abs_lowcore_mapped = true; } static struct resource code_resource = { diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 24f19f10b237..23c427284773 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -323,7 +323,6 @@ static void pcpu_delegate(struct pcpu *pcpu, { struct lowcore *lc, *abs_lc; unsigned int source_cpu; - unsigned long flags; lc = lowcore_ptr[pcpu - pcpu_devices]; source_cpu = stap(); @@ -341,12 +340,12 @@ static void pcpu_delegate(struct pcpu *pcpu, lc->restart_data = (unsigned long)data; lc->restart_source = source_cpu; } else { - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); abs_lc->restart_stack = stack; abs_lc->restart_fn = (unsigned long)func; abs_lc->restart_data = (unsigned long)data; abs_lc->restart_source = source_cpu; - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); } __bpon(); asm volatile( @@ -593,7 +592,6 @@ void smp_ctl_set_clear_bit(int cr, int bit, bool set) { struct ec_creg_mask_parms parms = { .cr = cr, }; struct lowcore *abs_lc; - unsigned long flags; u64 ctlreg; if (set) { @@ -604,11 +602,11 @@ void smp_ctl_set_clear_bit(int cr, int bit, bool set) parms.andval = ~(1UL << bit); } spin_lock(&ctl_lock); - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); ctlreg = abs_lc->cregs_save_area[cr]; ctlreg = (ctlreg & parms.andval) | parms.orval; abs_lc->cregs_save_area[cr] = ctlreg; - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); spin_unlock(&ctl_lock); on_each_cpu(smp_ctl_bit_callback, &parms, 1); } diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index 7c66b3ad05e0..d02a61620cfa 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -151,7 +151,6 @@ void *xlate_dev_mem_ptr(phys_addr_t addr) void *ptr = phys_to_virt(addr); void *bounce = ptr; struct lowcore *abs_lc; - unsigned long flags; unsigned long size; int this_cpu, cpu; @@ -167,10 +166,10 @@ void *xlate_dev_mem_ptr(phys_addr_t addr) goto out; size = PAGE_SIZE - (addr & ~PAGE_MASK); if (addr < sizeof(struct lowcore)) { - abs_lc = get_abs_lowcore(&flags); + abs_lc = get_abs_lowcore(); ptr = (void *)abs_lc + addr; memcpy(bounce, ptr, size); - put_abs_lowcore(abs_lc, flags); + put_abs_lowcore(abs_lc); } else if (cpu == this_cpu) { ptr = (void *)(addr - virt_to_phys(lowcore_ptr[cpu])); memcpy(bounce, ptr, size); From 760c6ce64b7355bf583928200db9a553c0751a11 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 8 Jan 2023 19:08:55 +0100 Subject: [PATCH 100/182] s390: move __amode31_base declaration to proper header file Move __amode31_base declaration to proper header file to get rid of arch/s390/boot/startup.c:24:15: warning: symbol '__amode31_base' was not declared. Should it be static? Signed-off-by: Heiko Carstens --- arch/s390/include/asm/setup.h | 1 + arch/s390/kernel/entry.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 6792ce28d37a..177bf6deaa27 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -76,6 +76,7 @@ extern unsigned long ident_map_size; extern unsigned long pgalloc_pos; extern unsigned long pgalloc_end; extern unsigned long pgalloc_low; +extern unsigned long __amode31_base; /* The Write Back bit position in the physaddr is given by the SLPC PCI */ extern unsigned long mio_wb_bit_mask; diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 995ec7449feb..34674e38826b 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -73,6 +73,5 @@ extern struct exception_table_entry _stop_amode31_ex_table[]; #define __amode31_data __section(".amode31.data") #define __amode31_ref __section(".amode31.refs") extern long _start_amode31_refs[], _end_amode31_refs[]; -extern unsigned long __amode31_base; #endif /* _ENTRY_H */ From 08866d34c7099e981918d34aab5d6a437436628f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 31 Jul 2022 18:09:14 +0200 Subject: [PATCH 101/182] s390/vfio-ap: fix an error handling path in vfio_ap_mdev_probe_queue() The commit in Fixes: has switch the order of a sysfs_create_group() and a kzalloc(). It correctly removed the now useless kfree() but forgot to add a sysfs_remove_group() in case of (unlikely) memory allocation failure. Add it now. Fixes: 260f3ea14138 ("s390/vfio-ap: move probe and remove callbacks to vfio_ap_ops.c") Signed-off-by: Christophe JAILLET Signed-off-by: Alexander Gordeev Link: https://lore.kernel.org/r/d0c0a35eec4fa87cb7f3910d8ac4dc0f7dc9008a.1659283738.git.christophe.jaillet@wanadoo.fr Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 9c01957e56b3..b0b25bc95985 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -1857,8 +1857,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev) return ret; q = kzalloc(sizeof(*q), GFP_KERNEL); - if (!q) - return -ENOMEM; + if (!q) { + ret = -ENOMEM; + goto err_remove_group; + } q->apqn = to_ap_queue(&apdev->device)->qid; q->saved_isc = VFIO_AP_ISC_INVALID; @@ -1876,6 +1878,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev) release_update_locks_for_mdev(matrix_mdev); return 0; + +err_remove_group: + sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group); + return ret; } void vfio_ap_mdev_remove_queue(struct ap_device *apdev) From ebc872d645e581a4774c36bc394f6246275e1547 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Mon, 2 Jan 2023 20:02:53 +0100 Subject: [PATCH 102/182] docs/ABI: use linux-s390 list as the main contact Remove Cornelia's email address from the file as suggested by her. List linux-s390 mailing-list address as the primary contact instead. Link: https://lore.kernel.org/linux-s390/8735d0oiq6.fsf@redhat.com/ Signed-off-by: Vineeth Vijayan Reviewed-by: Cornelia Huck Signed-off-by: Heiko Carstens --- Documentation/ABI/testing/sysfs-bus-css | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-css b/Documentation/ABI/testing/sysfs-bus-css index 12a733fe357f..d4d5cfb63b90 100644 --- a/Documentation/ABI/testing/sysfs-bus-css +++ b/Documentation/ABI/testing/sysfs-bus-css @@ -1,22 +1,19 @@ What: /sys/bus/css/devices/.../type Date: March 2008 -Contact: Cornelia Huck - linux-s390@vger.kernel.org +Contact: linux-s390@vger.kernel.org Description: Contains the subchannel type, as reported by the hardware. This attribute is present for all subchannel types. What: /sys/bus/css/devices/.../modalias Date: March 2008 -Contact: Cornelia Huck - linux-s390@vger.kernel.org +Contact: linux-s390@vger.kernel.org Description: Contains the module alias as reported with uevents. It is of the format css:t and present for all subchannel types. What: /sys/bus/css/drivers/io_subchannel/.../chpids Date: December 2002 -Contact: Cornelia Huck - linux-s390@vger.kernel.org +Contact: linux-s390@vger.kernel.org Description: Contains the ids of the channel paths used by this subchannel, as reported by the channel subsystem during subchannel recognition. @@ -26,8 +23,7 @@ Users: s390-tools, HAL What: /sys/bus/css/drivers/io_subchannel/.../pimpampom Date: December 2002 -Contact: Cornelia Huck - linux-s390@vger.kernel.org +Contact: linux-s390@vger.kernel.org Description: Contains the PIM/PAM/POM values, as reported by the channel subsystem when last queried by the common I/O layer (this implies that this attribute is not necessarily @@ -38,8 +34,7 @@ Users: s390-tools, HAL What: /sys/bus/css/devices/.../driver_override Date: June 2019 -Contact: Cornelia Huck - linux-s390@vger.kernel.org +Contact: linux-s390@vger.kernel.org Description: This file allows the driver for a device to be specified. When specified, only a driver with a name matching the value written to driver_override will have an opportunity to bind to the From c313094491150bae23b02270e83c72bb97ef2ab2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 14 Jan 2023 16:08:22 +0100 Subject: [PATCH 103/182] s390/ipl: use kstrtobool() instead of strtobool() strtobool() is the same as kstrtobool(). However, the latter is more used within the kernel. In order to remove strtobool() and slightly simplify kstrtox.h, switch to the other function name. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/58a3ed2e21903a93dfd742943b1e6936863ca037.1673708887.git.christophe.jaillet@wanadoo.fr Signed-off-by: Heiko Carstens --- arch/s390/kernel/ipl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index b74b728c29f8..406766c894fb 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1194,7 +1194,7 @@ static ssize_t reipl_eckd_clear_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t len) { - if (strtobool(buf, &reipl_eckd_clear) < 0) + if (kstrtobool(buf, &reipl_eckd_clear) < 0) return -EINVAL; return len; } From ca34cda73fd4c8fd93c488addc2eeb20af9e923f Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Thu, 17 Nov 2022 07:31:50 +0100 Subject: [PATCH 104/182] s390/cio: evaluate devices with non-operational paths css_schedule_reprobe() function calls the evaluation for CSS_EVAL_UNREG which is specific to the idset of unregistered subchannels. This evaluation was introduced because, previously, if the underlying device become not-accessible, the subchannel was unregistered. But, in the recent changes in cio,with the commit '2297791c92d0 s390/cio: dont unregister subchannel from child-drivers', we no longer unregister the subchannels just because of a non-operational device. This allows to have subchannels without any operational device connected on it. So, a css_schedule_reprobe function on unregistered subchannel does not have any effect. Change this functionality to evaluate the subchannels which does not have a working path to the device. This could be due the erroneous device or due to the erraneous path. Evaluate based on the values of OPM and PAM&POM. Here we introduced a new idset function,to keep I/O subchannels in the idset when the last seen status indicates that the device has no working path. A device has no working path if all available paths have been tried without success.A failed I/O attempt on a path is indicated as a 0 bit value in the POM mask. By looking at the POM mask bit values of available paths (1 in PAM) that Linux is supposed to use (1 in vary mask OPM), we can identify a non-working device as a device where the bit-wise and of the PAM, POM and OPM mask return 0. css_schedule_reprobe() is being used by dasd-driver and chsc-cio component. dasd driver, when it detects a change in the pathgroup, invokes the re-evaluation of the subchannel. And chsc-cio component upon a CRW event, (resource accessibility event). In both the cases, it makes much better sense to re-evalute the subchannel with no-valid path. Signed-off-by: Vineeth Vijayan Reported-by: Eric Farman Reviewed-by: Peter Oberparleiter Tested-by: Eric Farman Signed-off-by: Heiko Carstens --- drivers/s390/cio/css.c | 21 +++++++++++++++------ drivers/s390/cio/css.h | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c7db95398500..dfbb998db86f 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -740,12 +740,21 @@ void css_schedule_eval_all(void) spin_unlock_irqrestore(&slow_subchannel_lock, flags); } -static int __unset_registered(struct device *dev, void *data) +static int __unset_validpath(struct device *dev, void *data) { struct idset *set = data; struct subchannel *sch = to_subchannel(dev); + struct pmcw *pmcw = &sch->schib.pmcw; + + /* Here we want to make sure that we are considering only those subchannels + * which do not have an operational device attached to it. This can be found + * with the help of PAM and POM values of pmcw. OPM provides the information + * about any path which is currently vary-off, so that we should not consider. + */ + if (sch->st == SUBCHANNEL_TYPE_IO && + (sch->opm & pmcw->pam & pmcw->pom)) + idset_sch_del(set, sch->schid); - idset_sch_del(set, sch->schid); return 0; } @@ -774,8 +783,8 @@ void css_schedule_eval_cond(enum css_eval_cond cond, unsigned long delay) } idset_fill(set); switch (cond) { - case CSS_EVAL_UNREG: - bus_for_each_dev(&css_bus_type, NULL, set, __unset_registered); + case CSS_EVAL_NO_PATH: + bus_for_each_dev(&css_bus_type, NULL, set, __unset_validpath); break; case CSS_EVAL_NOT_ONLINE: bus_for_each_dev(&css_bus_type, NULL, set, __unset_online); @@ -798,11 +807,11 @@ void css_wait_for_slow_path(void) flush_workqueue(cio_work_q); } -/* Schedule reprobing of all unregistered subchannels. */ +/* Schedule reprobing of all subchannels with no valid operational path. */ void css_schedule_reprobe(void) { /* Schedule with a delay to allow merging of subsequent calls. */ - css_schedule_eval_cond(CSS_EVAL_UNREG, 1 * HZ); + css_schedule_eval_cond(CSS_EVAL_NO_PATH, 1 * HZ); } EXPORT_SYMBOL_GPL(css_schedule_reprobe); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ede0b905bc6f..ea5550554297 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -38,7 +38,7 @@ * Conditions used to specify which subchannels need evaluation */ enum css_eval_cond { - CSS_EVAL_UNREG, /* unregistered subchannels */ + CSS_EVAL_NO_PATH, /* Subchannels with no operational paths */ CSS_EVAL_NOT_ONLINE /* sch without an online-device */ }; From b4af09140a04ab5a4a20b3e14738ee56ddd37eff Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Tue, 17 Jan 2023 14:02:23 +0800 Subject: [PATCH 105/182] s390/vmem: use swap() instead of open coding it Swap is a function interface that provides exchange function. To avoid code duplication, we can use swap function. ./arch/s390/mm/vmem.c:680:10-11: WARNING opportunity for swap(). [hca@linux.ibm.com: get rid of all temp variables] Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3786 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20230117060223.58583-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Heiko Carstens --- arch/s390/mm/vmem.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 78d7768f93d7..15daf777cf41 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -672,13 +672,7 @@ static int __init memblock_region_cmp(const void *a, const void *b) static void __init memblock_region_swap(void *a, void *b, int size) { - struct memblock_region *r1 = a; - struct memblock_region *r2 = b; - struct memblock_region swap; - - swap = *r1; - *r1 = *r2; - *r2 = swap; + swap(*(struct memblock_region *)a, *(struct memblock_region *)b); } /* From a64e45c2ea62d9622bcebb586f359ea95f0a5152 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 12 Jan 2023 15:09:21 +0100 Subject: [PATCH 106/182] s390/cpum_sf: move functions from header file to source file Some inline helper functions are defined in a header file but used in only one source file. Move these functions to the source file. No functional change. Signed-off-by: Thomas Richter Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpu_mf.h | 53 --------------------------------- arch/s390/kernel/perf_cpum_sf.c | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/arch/s390/include/asm/cpu_mf.h b/arch/s390/include/asm/cpu_mf.h index efa103b52a1a..7e417d7de568 100644 --- a/arch/s390/include/asm/cpu_mf.h +++ b/arch/s390/include/asm/cpu_mf.h @@ -42,7 +42,6 @@ static inline int cpum_sf_avail(void) return test_facility(40) && test_facility(68); } - struct cpumf_ctr_info { u16 cfvn; u16 auth_ctl; @@ -275,56 +274,4 @@ static inline int lsctl(struct hws_lsctl_request_block *req) return cc ? -EINVAL : 0; } - -/* Sampling control helper functions */ - -#include - -static inline unsigned long freq_to_sample_rate(struct hws_qsi_info_block *qsi, - unsigned long freq) -{ - return (USEC_PER_SEC / freq) * qsi->cpu_speed; -} - -static inline unsigned long sample_rate_to_freq(struct hws_qsi_info_block *qsi, - unsigned long rate) -{ - return USEC_PER_SEC * qsi->cpu_speed / rate; -} - -/* Return TOD timestamp contained in an trailer entry */ -static inline unsigned long long trailer_timestamp(struct hws_trailer_entry *te) -{ - /* TOD in STCKE format */ - if (te->header.t) - return *((unsigned long long *) &te->timestamp[1]); - - /* TOD in STCK format */ - return *((unsigned long long *) &te->timestamp[0]); -} - -/* Return pointer to trailer entry of an sample data block */ -static inline unsigned long *trailer_entry_ptr(unsigned long v) -{ - void *ret; - - ret = (void *) v; - ret += PAGE_SIZE; - ret -= sizeof(struct hws_trailer_entry); - - return (unsigned long *) ret; -} - -/* Return true if the entry in the sample data block table (sdbt) - * is a link to the next sdbt */ -static inline int is_link_entry(unsigned long *s) -{ - return *s & 0x1ul ? 1 : 0; -} - -/* Return pointer to the linked sdbt */ -static inline unsigned long *get_next_sdbt(unsigned long *s) -{ - return (unsigned long *) (*s & ~0x1ul); -} #endif /* _ASM_S390_CPU_MF_H */ diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index ce886a03545a..934a45c85dc4 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -22,6 +22,7 @@ #include #include #include +#include /* Minimum number of sample-data-block-tables: * At least one table is required for the sampling buffer structure. @@ -99,6 +100,57 @@ static DEFINE_PER_CPU(struct cpu_hw_sf, cpu_hw_sf); /* Debug feature */ static debug_info_t *sfdbg; +/* Sampling control helper functions */ +static inline unsigned long freq_to_sample_rate(struct hws_qsi_info_block *qsi, + unsigned long freq) +{ + return (USEC_PER_SEC / freq) * qsi->cpu_speed; +} + +static inline unsigned long sample_rate_to_freq(struct hws_qsi_info_block *qsi, + unsigned long rate) +{ + return USEC_PER_SEC * qsi->cpu_speed / rate; +} + +/* Return TOD timestamp contained in an trailer entry */ +static inline unsigned long long trailer_timestamp(struct hws_trailer_entry *te) +{ + /* TOD in STCKE format */ + if (te->header.t) + return *((unsigned long long *)&te->timestamp[1]); + + /* TOD in STCK format */ + return *((unsigned long long *)&te->timestamp[0]); +} + +/* Return pointer to trailer entry of an sample data block */ +static inline unsigned long *trailer_entry_ptr(unsigned long v) +{ + void *ret; + + ret = (void *)v; + ret += PAGE_SIZE; + ret -= sizeof(struct hws_trailer_entry); + + return (unsigned long *)ret; +} + +/* + * Return true if the entry in the sample data block table (sdbt) + * is a link to the next sdbt + */ +static inline int is_link_entry(unsigned long *s) +{ + return *s & 0x1UL ? 1 : 0; +} + +/* Return pointer to the linked sdbt */ +static inline unsigned long *get_next_sdbt(unsigned long *s) +{ + return (unsigned long *)(*s & ~0x1UL); +} + /* * sf_disable() - Switch off sampling facility */ From 4012fc20e2c6b01c5cf53a7f9d7693da4c10de45 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 5 Jan 2023 14:11:33 +0100 Subject: [PATCH 107/182] s390/cpum_sf: remove debug statements from function setup_pmc_cpu Remove debug statements from function setup_pmc_cpu(). The debug statement displays a pointer value to a per cpu variable. This pointer value is printed nowhere else, so it has no use for cross reference. No functional change. Signed-off-by: Thomas Richter Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_sf.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 934a45c85dc4..09a8c7eed1f8 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -609,9 +609,6 @@ static void setup_pmc_cpu(void *flags) if (err) pr_err("Switching off the sampling facility failed " "with rc %i\n", err); - debug_sprintf_event(sfdbg, 5, - "%s: initialized: cpuhw %p\n", __func__, - cpusf); break; case PMC_RELEASE: cpusf->flags &= ~PMU_F_RESERVED; @@ -621,9 +618,6 @@ static void setup_pmc_cpu(void *flags) "with rc %i\n", err); } else deallocate_buffers(cpusf); - debug_sprintf_event(sfdbg, 5, - "%s: released: cpuhw %p\n", __func__, - cpusf); break; } if (err) From 1f8e50722fcc95ba65456706ff1b8e5ec72a65fd Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Mon, 16 Jan 2023 14:58:22 +0100 Subject: [PATCH 108/182] s390/cpum_sf: sampling buffer setup to handle virtual addresses The CPU Measurement Sampling Facility (CPUM_SF) installs large buffers to save samples collected by hardware. These buffers are organized as Sample Data Buffer Tables (SDBT) and Sample Data Buffers (SDB). SDBs contain the samples which are extracted and saved in the perf ring buffer. The SDBTs are chained using real addresses and refer to SDBs using real addresses. Adds proper virtual to phyiscal address translation to the buffer chaining. The current constraint which requires virtual equals real address layout is removed. Signed-off-by: Thomas Richter Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_sf.c | 40 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 09a8c7eed1f8..00d6f6f0e0eb 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -125,7 +125,7 @@ static inline unsigned long long trailer_timestamp(struct hws_trailer_entry *te) } /* Return pointer to trailer entry of an sample data block */ -static inline unsigned long *trailer_entry_ptr(unsigned long v) +static inline struct hws_trailer_entry *trailer_entry_ptr(unsigned long v) { void *ret; @@ -133,7 +133,7 @@ static inline unsigned long *trailer_entry_ptr(unsigned long v) ret += PAGE_SIZE; ret -= sizeof(struct hws_trailer_entry); - return (unsigned long *)ret; + return ret; } /* @@ -148,7 +148,7 @@ static inline int is_link_entry(unsigned long *s) /* Return pointer to the linked sdbt */ static inline unsigned long *get_next_sdbt(unsigned long *s) { - return (unsigned long *)(*s & ~0x1UL); + return phys_to_virt(*s & ~0x1UL); } /* @@ -202,7 +202,7 @@ static void free_sampling_buffer(struct sf_buffer *sfb) } else { /* Process SDB pointer */ if (*curr) { - free_page(*curr); + free_page((unsigned long)phys_to_virt(*curr)); curr++; } } @@ -222,11 +222,11 @@ static int alloc_sample_data_block(unsigned long *sdbt, gfp_t gfp_flags) sdb = get_zeroed_page(gfp_flags); if (!sdb) return -ENOMEM; - te = (struct hws_trailer_entry *)trailer_entry_ptr(sdb); + te = trailer_entry_ptr(sdb); te->header.a = 1; /* Link SDB into the sample-data-block-table */ - *sdbt = sdb; + *sdbt = virt_to_phys((void *)sdb); return 0; } @@ -285,7 +285,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb, } sfb->num_sdbt++; /* Link current page to tail of chain */ - *tail = (unsigned long)(void *) new + 1; + *tail = virt_to_phys((void *)new) + 1; tail_prev = tail; tail = new; } @@ -315,7 +315,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb, } /* Link sampling buffer to its origin */ - *tail = (unsigned long) sfb->sdbt + 1; + *tail = virt_to_phys(sfb->sdbt) + 1; sfb->tail = tail; debug_sprintf_event(sfdbg, 4, "%s: new buffer" @@ -353,7 +353,7 @@ static int alloc_sampling_buffer(struct sf_buffer *sfb, unsigned long num_sdb) * realloc_sampling_buffer() invocation. */ sfb->tail = sfb->sdbt; - *sfb->tail = (unsigned long)(void *) sfb->sdbt + 1; + *sfb->tail = virt_to_phys((void *)sfb->sdbt) + 1; /* Allocate requested number of sample-data-blocks */ rc = realloc_sampling_buffer(sfb, num_sdb, GFP_KERNEL); @@ -1222,8 +1222,8 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt, struct hws_trailer_entry *te; struct hws_basic_entry *sample; - te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt); - sample = (struct hws_basic_entry *) *sdbt; + te = trailer_entry_ptr((unsigned long)sdbt); + sample = (struct hws_basic_entry *)sdbt; while ((unsigned long *) sample < (unsigned long *) te) { /* Check for an empty sample */ if (!sample->def || sample->LS) @@ -1304,7 +1304,7 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) union hws_trailer_header old, prev, new; struct hw_perf_event *hwc = &event->hw; struct hws_trailer_entry *te; - unsigned long *sdbt; + unsigned long *sdbt, sdb; int done; /* @@ -1321,7 +1321,8 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) done = event_overflow = sampl_overflow = num_sdb = 0; while (!done) { /* Get the trailer entry of the sample-data-block */ - te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt); + sdb = (unsigned long)phys_to_virt(*sdbt); + te = trailer_entry_ptr(sdb); /* Leave loop if no more work to do (block full indicator) */ if (!te->header.f) { @@ -1339,16 +1340,17 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) sampl_overflow += te->header.overflow; /* Timestamps are valid for full sample-data-blocks only */ - debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx " + debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx/%#lx " "overflow %llu timestamp %#llx\n", - __func__, (unsigned long)sdbt, te->header.overflow, + __func__, sdb, (unsigned long)sdbt, + te->header.overflow, (te->header.f) ? trailer_timestamp(te) : 0ULL); /* Collect all samples from a single sample-data-block and * flag if an (perf) event overflow happened. If so, the PMU * is stopped and remaining samples will be discarded. */ - hw_collect_samples(event, sdbt, &event_overflow); + hw_collect_samples(event, (unsigned long *)sdb, &event_overflow); num_sdb++; /* Reset trailer (using compare-double-and-swap) */ @@ -1421,7 +1423,7 @@ static struct hws_trailer_entry *aux_sdb_trailer(struct aux_buffer *aux, index = AUX_SDB_INDEX(aux, index); sdb = aux->sdb_index[index]; - return (struct hws_trailer_entry *)trailer_entry_ptr(sdb); + return trailer_entry_ptr(sdb); } /* @@ -1747,7 +1749,7 @@ static void aux_sdb_init(unsigned long sdb) { struct hws_trailer_entry *te; - te = (struct hws_trailer_entry *)trailer_entry_ptr(sdb); + te = trailer_entry_ptr(sdb); /* Save clock base */ te->clock_base = 1; @@ -1978,7 +1980,7 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags) cpuhw->lsctl.h = 1; cpuhw->lsctl.interval = SAMPL_RATE(&event->hw); if (!SAMPL_DIAG_MODE(&event->hw)) { - cpuhw->lsctl.tear = (unsigned long) cpuhw->sfb.sdbt; + cpuhw->lsctl.tear = virt_to_phys(cpuhw->sfb.sdbt); cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt; TEAR_REG(&event->hw) = (unsigned long) cpuhw->sfb.sdbt; } From 78157b4791a3e37659b33388fb639a2defc6e6e9 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 13 Jan 2023 12:12:07 +0100 Subject: [PATCH 109/182] s390/cpum_sf: rework macro AUX_SDB_NUM_xxx Macro AUX_SDB_NUM() has three parameters. The first one is not used. Remove the first parameter. Also convert the macros to inline functions. No functional change. Signed-off-by: Thomas Richter Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_sf.c | 42 +++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 00d6f6f0e0eb..d7940a7dd7c3 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1408,10 +1408,26 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) OVERFLOW_REG(hwc), num_sdb); } -#define AUX_SDB_INDEX(aux, i) ((i) % aux->sfb.num_sdb) -#define AUX_SDB_NUM(aux, start, end) (end >= start ? end - start + 1 : 0) -#define AUX_SDB_NUM_ALERT(aux) AUX_SDB_NUM(aux, aux->head, aux->alert_mark) -#define AUX_SDB_NUM_EMPTY(aux) AUX_SDB_NUM(aux, aux->head, aux->empty_mark) +static inline unsigned long aux_sdb_index(struct aux_buffer *aux, + unsigned long i) +{ + return i % aux->sfb.num_sdb; +} + +static inline unsigned long aux_sdb_num(unsigned long start, unsigned long end) +{ + return end >= start ? end - start + 1 : 0; +} + +static inline unsigned long aux_sdb_num_alert(struct aux_buffer *aux) +{ + return aux_sdb_num(aux->head, aux->alert_mark); +} + +static inline unsigned long aux_sdb_num_empty(struct aux_buffer *aux) +{ + return aux_sdb_num(aux->head, aux->empty_mark); +} /* * Get trailer entry by index of SDB. @@ -1421,7 +1437,7 @@ static struct hws_trailer_entry *aux_sdb_trailer(struct aux_buffer *aux, { unsigned long sdb; - index = AUX_SDB_INDEX(aux, index); + index = aux_sdb_index(aux, index); sdb = aux->sdb_index[index]; return trailer_entry_ptr(sdb); } @@ -1445,7 +1461,7 @@ static void aux_output_end(struct perf_output_handle *handle) if (!aux) return; - range_scan = AUX_SDB_NUM_ALERT(aux); + range_scan = aux_sdb_num_alert(aux); for (i = 0, idx = aux->head; i < range_scan; i++, idx++) { te = aux_sdb_trailer(aux, idx); if (!te->header.f) @@ -1496,8 +1512,8 @@ static int aux_output_begin(struct perf_output_handle *handle, "%s: range %ld head %ld alert %ld empty %ld\n", __func__, range, aux->head, aux->alert_mark, aux->empty_mark); - if (range > AUX_SDB_NUM_EMPTY(aux)) { - range_scan = range - AUX_SDB_NUM_EMPTY(aux); + if (range > aux_sdb_num_empty(aux)) { + range_scan = range - aux_sdb_num_empty(aux); idx = aux->empty_mark + 1; for (i = 0; i < range_scan; i++, idx++) { te = aux_sdb_trailer(aux, idx); @@ -1515,7 +1531,7 @@ static int aux_output_begin(struct perf_output_handle *handle, te->header.a = 1; /* Reset hardware buffer head */ - head = AUX_SDB_INDEX(aux, aux->head); + head = aux_sdb_index(aux, aux->head); base = aux->sdbt_index[head / CPUM_SF_SDB_PER_TABLE]; offset = head % CPUM_SF_SDB_PER_TABLE; cpuhw->lsctl.tear = base + offset * sizeof(unsigned long); @@ -1597,7 +1613,7 @@ static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range, debug_sprintf_event(sfdbg, 6, "%s: range %ld head %ld alert %ld " "empty %ld\n", __func__, range, aux->head, aux->alert_mark, aux->empty_mark); - if (range <= AUX_SDB_NUM_EMPTY(aux)) + if (range <= aux_sdb_num_empty(aux)) /* * No need to scan. All SDBs in range are marked as empty. * Just set alert indicator. Should check race with hardware @@ -1618,7 +1634,7 @@ static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range, * Start scanning from one SDB behind empty_mark. If the new alert * indicator fall into this range, set it. */ - range_scan = range - AUX_SDB_NUM_EMPTY(aux); + range_scan = range - aux_sdb_num_empty(aux); idx_old = idx = aux->empty_mark + 1; for (i = 0; i < range_scan; i++, idx++) { te = aux_sdb_trailer(aux, idx); @@ -1665,7 +1681,7 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw) return; /* Inform user space new data arrived */ - size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT; + size = aux_sdb_num_alert(aux) << PAGE_SHIFT; debug_sprintf_event(sfdbg, 6, "%s: #alert %ld\n", __func__, size >> PAGE_SHIFT); perf_aux_output_end(handle, size); @@ -1707,7 +1723,7 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw) "overflow %lld\n", __func__, aux->head, range, overflow); } else { - size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT; + size = aux_sdb_num_alert(aux) << PAGE_SHIFT; perf_aux_output_end(&cpuhw->handle, size); debug_sprintf_event(sfdbg, 6, "%s: head %ld alert %ld " "already full, try another\n", From d924ecdb703765cdc75be5f28aaa1140b9106f84 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 13 Jan 2023 12:29:55 +0100 Subject: [PATCH 110/182] s390/cpum_sf: diagnostic sampling buffer setup to handle virtual addresses The CPU Measurement Sampling Facility (CPUM_SF) installs large buffers to save samples collected by hardware. These buffers are organized as Sample Data Buffer Tables (SDBT) and Sample Data Buffers (SDB). SDBs contain the samples which are extracted and saved in the perf ring buffer. The SDBTs are chained using real addresses and refer to SDBs using real addresses. The diagnostic sampling setup uses buffers provided by the process which invokes perf_event_open system call. The buffers are memory mapped. The buffers have been allocated by the kernel event subsystem. Add proper virtual to phyiscal address translation to the buffer chaining. The current constraint which requires virtual equals real address layout is removed. Signed-off-by: Thomas Richter Acked-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_sf.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index d7940a7dd7c3..d61cf80511e8 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1491,9 +1491,7 @@ static int aux_output_begin(struct perf_output_handle *handle, struct aux_buffer *aux, struct cpu_hw_sf *cpuhw) { - unsigned long range; - unsigned long i, range_scan, idx; - unsigned long head, base, offset; + unsigned long range, i, range_scan, idx, head, base, offset; struct hws_trailer_entry *te; if (WARN_ON_ONCE(handle->head & ~PAGE_MASK)) @@ -1534,8 +1532,8 @@ static int aux_output_begin(struct perf_output_handle *handle, head = aux_sdb_index(aux, aux->head); base = aux->sdbt_index[head / CPUM_SF_SDB_PER_TABLE]; offset = head % CPUM_SF_SDB_PER_TABLE; - cpuhw->lsctl.tear = base + offset * sizeof(unsigned long); - cpuhw->lsctl.dear = aux->sdb_index[head]; + cpuhw->lsctl.tear = virt_to_phys((void *)base) + offset * sizeof(unsigned long); + cpuhw->lsctl.dear = virt_to_phys((void *)aux->sdb_index[head]); debug_sprintf_event(sfdbg, 6, "%s: head %ld alert %ld empty %ld " "index %ld tear %#lx dear %#lx\n", __func__, @@ -1845,18 +1843,18 @@ static void *aux_buffer_setup(struct perf_event *event, void **pages, goto no_sdbt; aux->sdbt_index[sfb->num_sdbt++] = (unsigned long)new; /* Link current page to tail of chain */ - *tail = (unsigned long)(void *) new + 1; + *tail = virt_to_phys(new) + 1; tail = new; } /* Tail is the entry in a SDBT */ - *tail = (unsigned long)pages[i]; + *tail = virt_to_phys(pages[i]); aux->sdb_index[i] = (unsigned long)pages[i]; aux_sdb_init((unsigned long)pages[i]); } sfb->num_sdb = nr_pages; /* Link the last entry in the SDBT to the first SDBT */ - *tail = (unsigned long) sfb->sdbt + 1; + *tail = virt_to_phys(sfb->sdbt) + 1; sfb->tail = tail; /* From 1a280f48c0e403903cf0b4231c95b948e664f25a Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 17 Jan 2023 14:37:10 +0100 Subject: [PATCH 111/182] s390/kprobes: replace kretprobe with rethook That's an adaptation of commit f3a112c0c40d ("x86,rethook,kprobes: Replace kretprobe with rethook on x86") to s390. Replaces the kretprobe code with rethook on s390. With this patch, kretprobe on s390 uses the rethook instead of kretprobe specific trampoline code. Tested-by: Ilya Leoshkevich Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/Kconfig | 1 + arch/s390/include/asm/kprobes.h | 3 +-- arch/s390/include/asm/unwind.h | 10 ++++++---- arch/s390/kernel/Makefile | 1 + arch/s390/kernel/kprobes.c | 30 ------------------------------ arch/s390/kernel/mcount.S | 12 ++++++------ arch/s390/kernel/rethook.c | 33 +++++++++++++++++++++++++++++++++ arch/s390/kernel/stacktrace.c | 6 +++--- arch/s390/lib/test_unwind.c | 12 ++++++------ 9 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 arch/s390/kernel/rethook.c diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 7fd08755a1f9..933771b0b07a 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -187,6 +187,7 @@ config S390 select HAVE_KPROBES select HAVE_KPROBES_ON_FTRACE select HAVE_KRETPROBES + select HAVE_RETHOOK select HAVE_KVM select HAVE_LIVEPATCH select HAVE_MEMBLOCK_PHYS_MAP diff --git a/arch/s390/include/asm/kprobes.h b/arch/s390/include/asm/kprobes.h index 598095f4b924..6af75857156b 100644 --- a/arch/s390/include/asm/kprobes.h +++ b/arch/s390/include/asm/kprobes.h @@ -70,8 +70,7 @@ struct kprobe_ctlblk { }; void arch_remove_kprobe(struct kprobe *p); -void __kretprobe_trampoline(void); -void trampoline_probe_handler(struct pt_regs *regs); +unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs); int kprobe_fault_handler(struct pt_regs *regs, int trapnr); int kprobe_exceptions_notify(struct notifier_block *self, diff --git a/arch/s390/include/asm/unwind.h b/arch/s390/include/asm/unwind.h index 02462e7100c1..b8ecf04e3468 100644 --- a/arch/s390/include/asm/unwind.h +++ b/arch/s390/include/asm/unwind.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include @@ -43,13 +43,15 @@ struct unwind_state { bool error; }; -/* Recover the return address modified by kretprobe and ftrace_graph. */ +/* Recover the return address modified by rethook and ftrace_graph. */ static inline unsigned long unwind_recover_ret_addr(struct unwind_state *state, unsigned long ip) { ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *)state->sp); - if (is_kretprobe_trampoline(ip)) - ip = kretprobe_find_ret_addr(state->task, (void *)state->sp, &state->kr_cur); +#ifdef CONFIG_RETHOOK + if (is_rethook_trampoline(ip)) + ip = rethook_find_ret_addr(state->task, state->sp, &state->kr_cur); +#endif return ip; } diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 5e6a23299790..6065fabb781a 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes_insn_page.o obj-$(CONFIG_KPROBES) += mcount.o +obj-$(CONFIG_RETHOOK) += rethook.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 401f9c933ff9..5e713f318de3 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -281,16 +281,6 @@ static void pop_kprobe(struct kprobe_ctlblk *kcb) } NOKPROBE_SYMBOL(pop_kprobe); -void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) -{ - ri->ret_addr = (kprobe_opcode_t *)regs->gprs[14]; - ri->fp = (void *)regs->gprs[15]; - - /* Replace the return addr with trampoline addr */ - regs->gprs[14] = (unsigned long)&__kretprobe_trampoline; -} -NOKPROBE_SYMBOL(arch_prepare_kretprobe); - static void kprobe_reenter_check(struct kprobe_ctlblk *kcb, struct kprobe *p) { switch (kcb->kprobe_status) { @@ -371,26 +361,6 @@ static int kprobe_handler(struct pt_regs *regs) } NOKPROBE_SYMBOL(kprobe_handler); -void arch_kretprobe_fixup_return(struct pt_regs *regs, - kprobe_opcode_t *correct_ret_addr) -{ - /* Replace fake return address with real one. */ - regs->gprs[14] = (unsigned long)correct_ret_addr; -} -NOKPROBE_SYMBOL(arch_kretprobe_fixup_return); - -/* - * Called from __kretprobe_trampoline - */ -void trampoline_probe_handler(struct pt_regs *regs) -{ - kretprobe_trampoline_handler(regs, (void *)regs->gprs[15]); -} -NOKPROBE_SYMBOL(trampoline_probe_handler); - -/* assembler function that handles the kretprobes must not be probed itself */ -NOKPROBE_SYMBOL(__kretprobe_trampoline); - /* * Called after single-stepping. p->addr is the address of the * instruction whose first byte has been replaced by the "breakpoint" diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 4786bfe02144..43ff91073d2a 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -135,9 +135,9 @@ SYM_FUNC_END(return_to_handler) #endif #endif /* CONFIG_FUNCTION_TRACER */ -#ifdef CONFIG_KPROBES +#ifdef CONFIG_RETHOOK -SYM_FUNC_START(__kretprobe_trampoline) +SYM_FUNC_START(arch_rethook_trampoline) stg %r14,(__SF_GPRS+8*8)(%r15) lay %r15,-STACK_FRAME_SIZE(%r15) @@ -152,16 +152,16 @@ SYM_FUNC_START(__kretprobe_trampoline) epsw %r2,%r3 risbg %r3,%r2,0,31,32 stg %r3,STACK_PTREGS_PSW(%r15) - larl %r1,__kretprobe_trampoline + larl %r1,arch_rethook_trampoline stg %r1,STACK_PTREGS_PSW+8(%r15) lay %r2,STACK_PTREGS(%r15) - brasl %r14,trampoline_probe_handler + brasl %r14,arch_rethook_trampoline_callback mvc __SF_EMPTY(16,%r7),STACK_PTREGS_PSW(%r15) lmg %r0,%r15,STACK_PTREGS_GPRS(%r15) lpswe __SF_EMPTY(%r15) -SYM_FUNC_END(__kretprobe_trampoline) +SYM_FUNC_END(arch_rethook_trampoline) -#endif /* CONFIG_KPROBES */ +#endif /* CONFIG_RETHOOK */ diff --git a/arch/s390/kernel/rethook.c b/arch/s390/kernel/rethook.c new file mode 100644 index 000000000000..f2b6237bc35d --- /dev/null +++ b/arch/s390/kernel/rethook.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +void arch_rethook_prepare(struct rethook_node *rh, struct pt_regs *regs, bool mcount) +{ + rh->ret_addr = regs->gprs[14]; + rh->frame = regs->gprs[15]; + + /* Replace the return addr with trampoline addr */ + regs->gprs[14] = (unsigned long)&arch_rethook_trampoline; +} +NOKPROBE_SYMBOL(arch_rethook_prepare); + +void arch_rethook_fixup_return(struct pt_regs *regs, + unsigned long correct_ret_addr) +{ + /* Replace fake return address with real one. */ + regs->gprs[14] = correct_ret_addr; +} +NOKPROBE_SYMBOL(arch_rethook_fixup_return); + +/* + * Called from arch_rethook_trampoline + */ +unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs) +{ + return rethook_trampoline_handler(regs, regs->gprs[15]); +} +NOKPROBE_SYMBOL(arch_rethook_trampoline_callback); + +/* assembler function that handles the rethook must not be probed itself */ +NOKPROBE_SYMBOL(arch_rethook_trampoline); diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 7ee455e8e3d5..0787010139f7 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c @@ -40,12 +40,12 @@ int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, if (!addr) return -EINVAL; -#ifdef CONFIG_KPROBES +#ifdef CONFIG_RETHOOK /* - * Mark stacktraces with kretprobed functions on them + * Mark stacktraces with krethook functions on them * as unreliable. */ - if (state.ip == (unsigned long)__kretprobe_trampoline) + if (state.ip == (unsigned long)arch_rethook_trampoline) return -EINVAL; #endif diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index 5a053b393d5c..7231bf97b93a 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -47,7 +47,7 @@ static void print_backtrace(char *bt) static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, unsigned long sp) { - int frame_count, prev_is_func2, seen_func2_func1, seen_kretprobe_trampoline; + int frame_count, prev_is_func2, seen_func2_func1, seen_arch_rethook_trampoline; const int max_frames = 128; struct unwind_state state; size_t bt_pos = 0; @@ -63,7 +63,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, frame_count = 0; prev_is_func2 = 0; seen_func2_func1 = 0; - seen_kretprobe_trampoline = 0; + seen_arch_rethook_trampoline = 0; unwind_for_each_frame(&state, task, regs, sp) { unsigned long addr = unwind_get_return_address(&state); char sym[KSYM_SYMBOL_LEN]; @@ -89,8 +89,8 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1")) seen_func2_func1 = 1; prev_is_func2 = str_has_prefix(sym, "unwindme_func2"); - if (str_has_prefix(sym, "__kretprobe_trampoline+0x0/")) - seen_kretprobe_trampoline = 1; + if (str_has_prefix(sym, "arch_rethook_trampoline+0x0/")) + seen_arch_rethook_trampoline = 1; } /* Check the results. */ @@ -106,8 +106,8 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, kunit_err(current_test, "Maximum number of frames exceeded\n"); ret = -EINVAL; } - if (seen_kretprobe_trampoline) { - kunit_err(current_test, "__kretprobe_trampoline+0x0 in unwinding results\n"); + if (seen_arch_rethook_trampoline) { + kunit_err(current_test, "arch_rethook_trampoline+0x0 in unwinding results\n"); ret = -EINVAL; } if (ret || force_bt) From 62414d901c3afde307f293a3c7f46baa2ec8edec Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Wed, 18 Jan 2023 15:31:06 -0500 Subject: [PATCH 112/182] s390/vfio-ap: verify reset complete in separate function The vfio_ap_mdev_reset_queue() function contains a loop to verify that the reset successfully completes within 40ms. This patch moves that loop into a separate function. Signed-off-by: Tony Krowiak Reviewed-by: Jason J. Herne Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/r/20230118203111.529766-2-akrowiak@linux.ibm.com Signed-off-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index b0b25bc95985..785d07991da9 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -1598,12 +1598,30 @@ static struct vfio_ap_queue *vfio_ap_find_queue(int apqn) return q; } +static int apq_reset_check(struct vfio_ap_queue *q) +{ + int iters = 2; + struct ap_queue_status status; + + while (iters--) { + msleep(20); + status = ap_tapq(q->apqn, NULL); + if (status.queue_empty && !status.irq_enabled) + return 0; + } + WARN_ONCE(iters <= 0, + "timeout verifying reset of queue %02x.%04x (%u, %u, %u)", + AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn), + status.queue_empty, status.irq_enabled, status.response_code); + + return -EBUSY; +} + static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q, unsigned int retry) { struct ap_queue_status status; int ret; - int retry2 = 2; if (!q) return 0; @@ -1640,14 +1658,8 @@ static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q, } /* wait for the reset to take effect */ - while (retry2--) { - if (status.queue_empty && !status.irq_enabled) - break; - msleep(20); - status = ap_tapq(q->apqn, NULL); - } - WARN_ONCE(retry2 <= 0, "unable to verify reset of queue %02x.%04x", - AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn)); + if (!(status.queue_empty && !status.irq_enabled)) + ret = apq_reset_check(q); free_resources: vfio_ap_free_aqic_resources(q); From 0daf9878a7990058e74025493820bce0f67654c4 Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Wed, 18 Jan 2023 15:31:07 -0500 Subject: [PATCH 113/182] s390/vfio_ap: check TAPQ response code when waiting for queue reset The vfio_ap_mdev_reset_queue() function does not check the status response code returned form the PQAP(TAPQ) function when verifying the queue's status; consequently, there is no way of knowing whether verification failed because the wait time was exceeded, or because the PQAP(TAPQ) failed. This patch adds a function to check the status response code from the PQAP(TAPQ) instruction and logs an appropriate message if it fails. Signed-off-by: Tony Krowiak Reviewed-by: Jason J. Herne Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/r/20230118203111.529766-3-akrowiak@linux.ibm.com Signed-off-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 36 ++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 785d07991da9..7031e360411b 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -1598,23 +1598,49 @@ static struct vfio_ap_queue *vfio_ap_find_queue(int apqn) return q; } +static int apq_status_check(int apqn, struct ap_queue_status *status) +{ + switch (status->response_code) { + case AP_RESPONSE_NORMAL: + case AP_RESPONSE_RESET_IN_PROGRESS: + if (status->queue_empty && !status->irq_enabled) + return 0; + return -EBUSY; + case AP_RESPONSE_DECONFIGURED: + /* + * If the AP queue is deconfigured, any subsequent AP command + * targeting the queue will fail with the same response code. On the + * other hand, when an AP adapter is deconfigured, the associated + * queues are reset, so let's return a value indicating the reset + * for which we're waiting completed successfully. + */ + return 0; + default: + WARN(true, + "failed to verify reset of queue %02x.%04x: TAPQ rc=%u\n", + AP_QID_CARD(apqn), AP_QID_QUEUE(apqn), + status->response_code); + return -EIO; + } +} + static int apq_reset_check(struct vfio_ap_queue *q) { - int iters = 2; + int iters = 2, ret; struct ap_queue_status status; while (iters--) { msleep(20); status = ap_tapq(q->apqn, NULL); - if (status.queue_empty && !status.irq_enabled) - return 0; + ret = apq_status_check(q->apqn, &status); + if (ret != -EBUSY) + return ret; } WARN_ONCE(iters <= 0, "timeout verifying reset of queue %02x.%04x (%u, %u, %u)", AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn), status.queue_empty, status.irq_enabled, status.response_code); - - return -EBUSY; + return ret; } static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q, From 3ba41768105c70faa1e1677c173a9eedc31c5094 Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Wed, 18 Jan 2023 15:31:08 -0500 Subject: [PATCH 114/182] s390/vfio_ap: use TAPQ to verify reset in progress completes To eliminate the repeated calls to the PQAP(ZAPQ) function to verify that a reset in progress completed successfully and ensure that error response codes get appropriately logged, let's call the apq_reset_check() function when the ZAPQ response code indicates that a reset that is already in progress. Signed-off-by: Tony Krowiak Reviewed-by: Jason J. Herne Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/r/20230118203111.529766-4-akrowiak@linux.ibm.com Signed-off-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 7031e360411b..bc9a814e6508 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -33,7 +33,7 @@ static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable); static struct vfio_ap_queue *vfio_ap_find_queue(int apqn); static const struct vfio_device_ops vfio_ap_matrix_dev_ops; -static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q, unsigned int retry); +static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q); /** * get_update_locks_for_kvm: Acquire the locks required to dynamically update a @@ -1643,8 +1643,7 @@ static int apq_reset_check(struct vfio_ap_queue *q) return ret; } -static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q, - unsigned int retry) +static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) { struct ap_queue_status status; int ret; @@ -1659,12 +1658,15 @@ static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q, ret = 0; break; case AP_RESPONSE_RESET_IN_PROGRESS: - if (retry--) { - msleep(20); - goto retry_zapq; - } - ret = -EBUSY; - break; + /* + * There is a reset issued by another process in progress. Let's wait + * for that to complete. Since we have no idea whether it was a RAPQ or + * ZAPQ, then if it completes successfully, let's issue the ZAPQ. + */ + ret = apq_reset_check(q); + if (ret) + break; + goto retry_zapq; case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: @@ -1699,7 +1701,7 @@ static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable) struct vfio_ap_queue *q; hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) { - ret = vfio_ap_mdev_reset_queue(q, 1); + ret = vfio_ap_mdev_reset_queue(q); /* * Regardless whether a queue turns out to be busy, or * is not operational, we need to continue resetting @@ -1950,7 +1952,7 @@ void vfio_ap_mdev_remove_queue(struct ap_device *apdev) } } - vfio_ap_mdev_reset_queue(q, 1); + vfio_ap_mdev_reset_queue(q); dev_set_drvdata(&apdev->device, NULL); kfree(q); release_update_locks_for_mdev(matrix_mdev); From 5a42b348adf9fbdd24166351046fc9fe317d1d0f Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Wed, 18 Jan 2023 15:31:09 -0500 Subject: [PATCH 115/182] s390/vfio_ap: verify ZAPQ completion after return of response code zero Verification that the asynchronous ZAPQ function has completed only needs to be done when the response code indicates the function was successfully initiated; so, let's call the apq_reset_check function immediately after the response code zero is returned from the ZAPQ. Signed-off-by: Tony Krowiak Reviewed-by: Jason J. Herne Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/r/20230118203111.529766-5-akrowiak@linux.ibm.com Signed-off-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index bc9a814e6508..6ed9fe77793e 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -1656,6 +1656,9 @@ static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) switch (status.response_code) { case AP_RESPONSE_NORMAL: ret = 0; + /* if the reset has not completed, wait for it to take effect */ + if (!status.queue_empty || status.irq_enabled) + ret = apq_reset_check(q); break; case AP_RESPONSE_RESET_IN_PROGRESS: /* @@ -1685,10 +1688,6 @@ static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) return -EIO; } - /* wait for the reset to take effect */ - if (!(status.queue_empty && !status.irq_enabled)) - ret = apq_reset_check(q); - free_resources: vfio_ap_free_aqic_resources(q); From 51d4d9877087685ac577a4a314f85e1b2046cae6 Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Wed, 18 Jan 2023 15:31:10 -0500 Subject: [PATCH 116/182] s390/vfio_ap: fix handling of error response codes Some response codes returned from the queue reset function are not being handled correctly; this patch fixes them: 1. Response code 3, AP queue deconfigured: Deconfiguring an AP adapter resets all of its queues, so this is handled by indicating the reset verification completed successfully. 2. For all response codes other than 0 (normal reset completion), 2 (queue reset in progress) and 3 (AP deconfigured), the -EIO error will be returned from the vfio_ap_mdev_reset_queue() function. In all cases, all fields of the status word other than the response code will be set to zero, so it makes no sense to check status bits. Signed-off-by: Tony Krowiak Reviewed-by: Jason J. Herne Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/r/20230118203111.529766-6-akrowiak@linux.ibm.com Signed-off-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 6ed9fe77793e..3bf012b3508e 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -1670,17 +1670,15 @@ static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) if (ret) break; goto retry_zapq; - case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: - case AP_RESPONSE_CHECKSTOPPED: - WARN_ONCE(status.irq_enabled, - "PQAP/ZAPQ for %02x.%04x failed with rc=%u while IRQ enabled", - AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn), - status.response_code); - ret = -EBUSY; - goto free_resources; + /* + * When an AP adapter is deconfigured, the associated + * queues are reset, so let's return a value indicating the reset + * completed successfully. + */ + ret = 0; + break; default: - /* things are really broken, give up */ WARN(true, "PQAP/ZAPQ for %02x.%04x failed with invalid rc=%u\n", AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn), @@ -1688,7 +1686,6 @@ static int vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q) return -EIO; } -free_resources: vfio_ap_free_aqic_resources(q); return ret; From 7cb7636a1ac158c00f9da8f09e333c1ddd881eca Mon Sep 17 00:00:00 2001 From: Tony Krowiak Date: Wed, 18 Jan 2023 15:31:11 -0500 Subject: [PATCH 117/182] s390/vfio_ap: increase max wait time for reset verification Increase the maximum time to wait for verification of a queue reset operation to 200ms. Signed-off-by: Tony Krowiak Reviewed-by: Jason J. Herne Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/r/20230118203111.529766-7-akrowiak@linux.ibm.com Signed-off-by: Christian Borntraeger Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 3bf012b3508e..cd488639a15b 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -30,6 +30,9 @@ #define AP_QUEUE_UNASSIGNED "unassigned" #define AP_QUEUE_IN_USE "in use" +#define MAX_RESET_CHECK_WAIT 200 /* Sleep max 200ms for reset check */ +#define AP_RESET_INTERVAL 20 /* Reset sleep interval (20ms) */ + static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable); static struct vfio_ap_queue *vfio_ap_find_queue(int apqn); static const struct vfio_device_ops vfio_ap_matrix_dev_ops; @@ -1626,11 +1629,12 @@ static int apq_status_check(int apqn, struct ap_queue_status *status) static int apq_reset_check(struct vfio_ap_queue *q) { - int iters = 2, ret; + int ret; + int iters = MAX_RESET_CHECK_WAIT / AP_RESET_INTERVAL; struct ap_queue_status status; - while (iters--) { - msleep(20); + for (; iters > 0; iters--) { + msleep(AP_RESET_INTERVAL); status = ap_tapq(q->apqn, NULL); ret = apq_status_check(q->apqn, &status); if (ret != -EBUSY) From 7a725b770271deba6c288d075abc8dc9d5aa61d0 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 22 Jan 2023 19:13:03 +0100 Subject: [PATCH 118/182] s390/cache: change type from unsigned long long to unsigned long The unsigned long long type is a leftover of the 31 bit area. Get rid of it. Signed-off-by: Heiko Carstens --- arch/s390/kernel/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c index 7ee3651d00ab..56254fa06f99 100644 --- a/arch/s390/kernel/cache.c +++ b/arch/s390/kernel/cache.c @@ -46,7 +46,7 @@ struct cache_info { #define CACHE_MAX_LEVEL 8 union cache_topology { struct cache_info ci[CACHE_MAX_LEVEL]; - unsigned long long raw; + unsigned long raw; }; static const char * const cache_type_string[] = { From 1ce357cb825f184519cf1d3c2b01581a0b97663c Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 24 Jan 2023 12:20:50 +0100 Subject: [PATCH 119/182] s390/cpum_cf: simplify hw_perf_event_destroy() To remove an event from the CPU Measurement counter facility use the lock/unlock scheme as done in event creation. Remove the atomic_add_unless function to make the code easier. Signed-off-by: Thomas Richter Acked-by: Hendrik Brueckner Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_cf.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 28fa80fd69fa..e58e49488ad9 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -397,12 +397,10 @@ static DEFINE_MUTEX(pmc_reserve_mutex); /* Release the PMU if event is the last perf event */ static void hw_perf_event_destroy(struct perf_event *event) { - if (!atomic_add_unless(&num_events, -1, 1)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_dec_return(&num_events) == 0) - __kernel_cpumcf_end(); - mutex_unlock(&pmc_reserve_mutex); - } + mutex_lock(&pmc_reserve_mutex); + if (atomic_dec_return(&num_events) == 0) + __kernel_cpumcf_end(); + mutex_unlock(&pmc_reserve_mutex); } /* CPUMF <-> perf event mappings for kernel+userspace (basic set) */ From 345d2a4dcdb7d0f33ebd990a19aeb3f3f458817d Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 24 Jan 2023 12:20:51 +0100 Subject: [PATCH 120/182] s390/cpum_cf: move cpum_cf_ctrset_size() Function cpum_cf_ctrset_size() is defined in one source file and the only user is in another source file. Move this function to the source file where it is used and remove its prototype from the header file. No functional change. Signed-off-by: Thomas Richter Acked-by: Hendrik Brueckner Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpu_mcf.h | 2 -- arch/s390/kernel/perf_cpum_cf.c | 47 ++++++++++++++++++++++++++ arch/s390/kernel/perf_cpum_cf_common.c | 46 ------------------------- 3 files changed, 47 insertions(+), 48 deletions(-) diff --git a/arch/s390/include/asm/cpu_mcf.h b/arch/s390/include/asm/cpu_mcf.h index f87a4788c19c..387e169597b0 100644 --- a/arch/s390/include/asm/cpu_mcf.h +++ b/arch/s390/include/asm/cpu_mcf.h @@ -105,8 +105,6 @@ static inline int stccm_avail(void) return test_facility(142); } -size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset, - struct cpumf_ctr_info *info); int cfset_online_cpu(unsigned int cpu); int cfset_offline_cpu(unsigned int cpu); #endif /* _ASM_S390_CPU_MCF_H */ diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index e58e49488ad9..936eda2e5c05 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -112,6 +112,53 @@ static void cfdiag_trailer(struct cf_trailer_entry *te) te->timestamp = get_tod_clock_fast(); } +/* + * Return the maximum possible counter set size (in number of 8 byte counters) + * depending on type and model number. + */ +static size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset, + struct cpumf_ctr_info *info) +{ + size_t ctrset_size = 0; + + switch (ctrset) { + case CPUMF_CTR_SET_BASIC: + if (info->cfvn >= 1) + ctrset_size = 6; + break; + case CPUMF_CTR_SET_USER: + if (info->cfvn == 1) + ctrset_size = 6; + else if (info->cfvn >= 3) + ctrset_size = 2; + break; + case CPUMF_CTR_SET_CRYPTO: + if (info->csvn >= 1 && info->csvn <= 5) + ctrset_size = 16; + else if (info->csvn == 6 || info->csvn == 7) + ctrset_size = 20; + break; + case CPUMF_CTR_SET_EXT: + if (info->csvn == 1) + ctrset_size = 32; + else if (info->csvn == 2) + ctrset_size = 48; + else if (info->csvn >= 3 && info->csvn <= 5) + ctrset_size = 128; + else if (info->csvn == 6 || info->csvn == 7) + ctrset_size = 160; + break; + case CPUMF_CTR_SET_MT_DIAG: + if (info->csvn > 3) + ctrset_size = 48; + break; + case CPUMF_CTR_SET_MAX: + break; + } + + return ctrset_size; +} + /* Read a counter set. The counter set number determines the counter set and * the CPUM-CF first and second version number determine the number of * available counters in each counter set. diff --git a/arch/s390/kernel/perf_cpum_cf_common.c b/arch/s390/kernel/perf_cpum_cf_common.c index 8ee48672233f..4824e3461b5b 100644 --- a/arch/s390/kernel/perf_cpum_cf_common.c +++ b/arch/s390/kernel/perf_cpum_cf_common.c @@ -156,52 +156,6 @@ static int cpum_cf_offline_cpu(unsigned int cpu) return cpum_cf_setup(cpu, PMC_RELEASE); } -/* Return the maximum possible counter set size (in number of 8 byte counters) - * depending on type and model number. - */ -size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset, - struct cpumf_ctr_info *info) -{ - size_t ctrset_size = 0; - - switch (ctrset) { - case CPUMF_CTR_SET_BASIC: - if (info->cfvn >= 1) - ctrset_size = 6; - break; - case CPUMF_CTR_SET_USER: - if (info->cfvn == 1) - ctrset_size = 6; - else if (info->cfvn >= 3) - ctrset_size = 2; - break; - case CPUMF_CTR_SET_CRYPTO: - if (info->csvn >= 1 && info->csvn <= 5) - ctrset_size = 16; - else if (info->csvn == 6 || info->csvn == 7) - ctrset_size = 20; - break; - case CPUMF_CTR_SET_EXT: - if (info->csvn == 1) - ctrset_size = 32; - else if (info->csvn == 2) - ctrset_size = 48; - else if (info->csvn >= 3 && info->csvn <= 5) - ctrset_size = 128; - else if (info->csvn == 6 || info->csvn == 7) - ctrset_size = 160; - break; - case CPUMF_CTR_SET_MT_DIAG: - if (info->csvn > 3) - ctrset_size = 48; - break; - case CPUMF_CTR_SET_MAX: - break; - } - - return ctrset_size; -} - static int __init cpum_cf_init(void) { int rc; From 7a8f09ac1850b17ca0cc9e1e4d6621a64661347e Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 24 Jan 2023 12:20:52 +0100 Subject: [PATCH 121/182] s390/cpum_cf: move stccm_avail() Function stccm_avail() is defined in a header file and the only user is one single source file. Move this function to the source file where it is also used and remove it from the header file. No functional change. Signed-off-by: Thomas Richter Acked-by: Hendrik Brueckner Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpu_mcf.h | 6 ------ arch/s390/kernel/perf_cpum_cf.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/s390/include/asm/cpu_mcf.h b/arch/s390/include/asm/cpu_mcf.h index 387e169597b0..4b29c31c0e04 100644 --- a/arch/s390/include/asm/cpu_mcf.h +++ b/arch/s390/include/asm/cpu_mcf.h @@ -99,12 +99,6 @@ static inline void kernel_cpumcf_end(void) preempt_enable(); } -/* Return true if store counter set multiple instruction is available */ -static inline int stccm_avail(void) -{ - return test_facility(142); -} - int cfset_online_cpu(unsigned int cpu); int cfset_offline_cpu(unsigned int cpu); #endif /* _ASM_S390_CPU_MCF_H */ diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 936eda2e5c05..7299f7f0b325 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -821,6 +821,12 @@ static struct pmu cpumf_pmu = { .read = cpumf_pmu_read, }; +/* Return true if store counter set multiple instruction is available */ +static inline int stccm_avail(void) +{ + return test_facility(142); +} + static int cfset_init(void); static int __init cpumf_pmu_init(void) { From ea53e6995f45e857fd34e4fbfbd436b5457da5f7 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 24 Jan 2023 12:20:53 +0100 Subject: [PATCH 122/182] s390/cpum_cf: remove in-kernel counting facility interface Commit 17bebcc68eee ("s390/cpum_cf: Add minimal in-kernel interface for counter measurements") introduced a small in-kernel interface for CPU Measurement counter facility. There are no users of this interface, therefore remove it. The following functions are removed: kernel_cpumcf_alert(), kernel_cpumcf_begin(), kernel_cpumcf_end(), kernel_cpumcf_avail() there is no need for them anymore. With the removal of function kernel_cpumcf_alert(), also remove member alert in struct cpu_cf_events. Its purpose was to counter measurement alert interrupts for the in-kernel interface. Signed-off-by: Thomas Richter Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpu_mcf.h | 17 ----------------- arch/s390/kernel/perf_cpum_cf.c | 2 +- arch/s390/kernel/perf_cpum_cf_common.c | 24 ------------------------ 3 files changed, 1 insertion(+), 42 deletions(-) diff --git a/arch/s390/include/asm/cpu_mcf.h b/arch/s390/include/asm/cpu_mcf.h index 4b29c31c0e04..88fc115ebfbe 100644 --- a/arch/s390/include/asm/cpu_mcf.h +++ b/arch/s390/include/asm/cpu_mcf.h @@ -67,7 +67,6 @@ static inline int ctr_stcctm(enum cpumf_ctr_set set, u64 range, u64 *dest) struct cpu_cf_events { struct cpumf_ctr_info info; atomic_t ctr_set[CPUMF_CTR_SET_MAX]; - atomic64_t alert; u64 state; /* For perf_event_open SVC */ u64 dev_state; /* For /dev/hwctr */ unsigned int flags; @@ -80,25 +79,9 @@ struct cpu_cf_events { }; DECLARE_PER_CPU(struct cpu_cf_events, cpu_cf_events); -bool kernel_cpumcf_avail(void); int __kernel_cpumcf_begin(void); -unsigned long kernel_cpumcf_alert(int clear); void __kernel_cpumcf_end(void); -static inline int kernel_cpumcf_begin(void) -{ - if (!cpum_cf_avail()) - return -ENODEV; - - preempt_disable(); - return __kernel_cpumcf_begin(); -} -static inline void kernel_cpumcf_end(void) -{ - __kernel_cpumcf_end(); - preempt_enable(); -} - int cfset_online_cpu(unsigned int cpu); int cfset_offline_cpu(unsigned int cpu); #endif /* _ASM_S390_CPU_MCF_H */ diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 7299f7f0b325..5eaa81dd56c3 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -832,7 +832,7 @@ static int __init cpumf_pmu_init(void) { int rc; - if (!kernel_cpumcf_avail()) + if (!cpum_cf_avail()) return -ENODEV; /* Setup s390dbf facility */ diff --git a/arch/s390/kernel/perf_cpum_cf_common.c b/arch/s390/kernel/perf_cpum_cf_common.c index 4824e3461b5b..99523dc3fc8b 100644 --- a/arch/s390/kernel/perf_cpum_cf_common.c +++ b/arch/s390/kernel/perf_cpum_cf_common.c @@ -27,7 +27,6 @@ DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events) = { [CPUMF_CTR_SET_EXT] = ATOMIC_INIT(0), [CPUMF_CTR_SET_MT_DIAG] = ATOMIC_INIT(0), }, - .alert = ATOMIC64_INIT(0), .state = 0, .dev_state = 0, .flags = 0, @@ -67,9 +66,6 @@ static void cpumf_measurement_alert(struct ext_code ext_code, if (alert & CPU_MF_INT_CF_MTDA) pr_warn("CPU[%i] MT counter data was lost\n", smp_processor_id()); - - /* store alert for special handling by in-kernel users */ - atomic64_or(alert, &cpuhw->alert); } #define PMC_INIT 0 @@ -94,12 +90,6 @@ static void cpum_cf_setup_cpu(void *flags) lcctl(0); } -bool kernel_cpumcf_avail(void) -{ - return cpum_cf_initalized; -} -EXPORT_SYMBOL(kernel_cpumcf_avail); - /* Initialize the CPU-measurement counter facility */ int __kernel_cpumcf_begin(void) { @@ -112,20 +102,6 @@ int __kernel_cpumcf_begin(void) } EXPORT_SYMBOL(__kernel_cpumcf_begin); -/* Obtain the CPU-measurement alerts for the counter facility */ -unsigned long kernel_cpumcf_alert(int clear) -{ - struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events); - unsigned long alert; - - alert = atomic64_read(&cpuhw->alert); - if (clear) - atomic64_set(&cpuhw->alert, 0); - - return alert; -} -EXPORT_SYMBOL(kernel_cpumcf_alert); - /* Release the CPU-measurement counter facility */ void __kernel_cpumcf_end(void) { From 1e99c242acb2fc211aa9f57cd1060622e66bbf63 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 24 Jan 2023 12:20:54 +0100 Subject: [PATCH 123/182] s390/cpum_cf: merge source files for CPU Measurement counter facility With no in-kernel user, the source files can be merged. Move all functions and the variable definitions to file perf_cpum_cf.c This file now contains all the necessary functions and definitions for the CPU Measurement counter facility device driver. The files cpu_mcf.h and perf_cpum_cf_common.c are deleted. Signed-off-by: Thomas Richter Acked-by: Hendrik Brueckner Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cpu_mcf.h | 87 ---------- arch/s390/kernel/Makefile | 2 +- arch/s390/kernel/perf_cpum_cf.c | 214 ++++++++++++++++++++++++- arch/s390/kernel/perf_cpum_cf_common.c | 163 ------------------- arch/s390/kernel/perf_pai_ext.c | 2 +- 5 files changed, 208 insertions(+), 260 deletions(-) delete mode 100644 arch/s390/include/asm/cpu_mcf.h delete mode 100644 arch/s390/kernel/perf_cpum_cf_common.c diff --git a/arch/s390/include/asm/cpu_mcf.h b/arch/s390/include/asm/cpu_mcf.h deleted file mode 100644 index 88fc115ebfbe..000000000000 --- a/arch/s390/include/asm/cpu_mcf.h +++ /dev/null @@ -1,87 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Counter facility support definitions for the Linux perf - * - * Copyright IBM Corp. 2019 - * Author(s): Hendrik Brueckner - */ -#ifndef _ASM_S390_CPU_MCF_H -#define _ASM_S390_CPU_MCF_H - -#include -#include - -enum cpumf_ctr_set { - CPUMF_CTR_SET_BASIC = 0, /* Basic Counter Set */ - CPUMF_CTR_SET_USER = 1, /* Problem-State Counter Set */ - CPUMF_CTR_SET_CRYPTO = 2, /* Crypto-Activity Counter Set */ - CPUMF_CTR_SET_EXT = 3, /* Extended Counter Set */ - CPUMF_CTR_SET_MT_DIAG = 4, /* MT-diagnostic Counter Set */ - - /* Maximum number of counter sets */ - CPUMF_CTR_SET_MAX, -}; - -#define CPUMF_LCCTL_ENABLE_SHIFT 16 -#define CPUMF_LCCTL_ACTCTL_SHIFT 0 - -static inline void ctr_set_enable(u64 *state, u64 ctrsets) -{ - *state |= ctrsets << CPUMF_LCCTL_ENABLE_SHIFT; -} - -static inline void ctr_set_disable(u64 *state, u64 ctrsets) -{ - *state &= ~(ctrsets << CPUMF_LCCTL_ENABLE_SHIFT); -} - -static inline void ctr_set_start(u64 *state, u64 ctrsets) -{ - *state |= ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT; -} - -static inline void ctr_set_stop(u64 *state, u64 ctrsets) -{ - *state &= ~(ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT); -} - -static inline int ctr_stcctm(enum cpumf_ctr_set set, u64 range, u64 *dest) -{ - switch (set) { - case CPUMF_CTR_SET_BASIC: - return stcctm(BASIC, range, dest); - case CPUMF_CTR_SET_USER: - return stcctm(PROBLEM_STATE, range, dest); - case CPUMF_CTR_SET_CRYPTO: - return stcctm(CRYPTO_ACTIVITY, range, dest); - case CPUMF_CTR_SET_EXT: - return stcctm(EXTENDED, range, dest); - case CPUMF_CTR_SET_MT_DIAG: - return stcctm(MT_DIAG_CLEARING, range, dest); - case CPUMF_CTR_SET_MAX: - return 3; - } - return 3; -} - -struct cpu_cf_events { - struct cpumf_ctr_info info; - atomic_t ctr_set[CPUMF_CTR_SET_MAX]; - u64 state; /* For perf_event_open SVC */ - u64 dev_state; /* For /dev/hwctr */ - unsigned int flags; - size_t used; /* Bytes used in data */ - size_t usedss; /* Bytes used in start/stop */ - unsigned char start[PAGE_SIZE]; /* Counter set at event add */ - unsigned char stop[PAGE_SIZE]; /* Counter set at event delete */ - unsigned char data[PAGE_SIZE]; /* Counter set at /dev/hwctr */ - unsigned int sets; /* # Counter set saved in memory */ -}; -DECLARE_PER_CPU(struct cpu_cf_events, cpu_cf_events); - -int __kernel_cpumcf_begin(void); -void __kernel_cpumcf_end(void); - -int cfset_online_cpu(unsigned int cpu); -int cfset_offline_cpu(unsigned int cpu); -#endif /* _ASM_S390_CPU_MCF_H */ diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 6065fabb781a..8983837b3565 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -70,7 +70,7 @@ obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o -obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf_common.o +obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o obj-$(CONFIG_PERF_EVENTS) += perf_pai_crypto.o perf_pai_ext.o diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 5eaa81dd56c3..940c717e6531 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -2,7 +2,7 @@ /* * Performance event support for s390x - CPU-measurement Counter Facility * - * Copyright IBM Corp. 2012, 2022 + * Copyright IBM Corp. 2012, 2023 * Author(s): Hendrik Brueckner * Thomas Richter */ @@ -16,11 +16,82 @@ #include #include #include +#include -#include +#include #include #include +enum cpumf_ctr_set { + CPUMF_CTR_SET_BASIC = 0, /* Basic Counter Set */ + CPUMF_CTR_SET_USER = 1, /* Problem-State Counter Set */ + CPUMF_CTR_SET_CRYPTO = 2, /* Crypto-Activity Counter Set */ + CPUMF_CTR_SET_EXT = 3, /* Extended Counter Set */ + CPUMF_CTR_SET_MT_DIAG = 4, /* MT-diagnostic Counter Set */ + + /* Maximum number of counter sets */ + CPUMF_CTR_SET_MAX, +}; + +#define CPUMF_LCCTL_ENABLE_SHIFT 16 +#define CPUMF_LCCTL_ACTCTL_SHIFT 0 + +static inline void ctr_set_enable(u64 *state, u64 ctrsets) +{ + *state |= ctrsets << CPUMF_LCCTL_ENABLE_SHIFT; +} + +static inline void ctr_set_disable(u64 *state, u64 ctrsets) +{ + *state &= ~(ctrsets << CPUMF_LCCTL_ENABLE_SHIFT); +} + +static inline void ctr_set_start(u64 *state, u64 ctrsets) +{ + *state |= ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT; +} + +static inline void ctr_set_stop(u64 *state, u64 ctrsets) +{ + *state &= ~(ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT); +} + +static inline int ctr_stcctm(enum cpumf_ctr_set set, u64 range, u64 *dest) +{ + switch (set) { + case CPUMF_CTR_SET_BASIC: + return stcctm(BASIC, range, dest); + case CPUMF_CTR_SET_USER: + return stcctm(PROBLEM_STATE, range, dest); + case CPUMF_CTR_SET_CRYPTO: + return stcctm(CRYPTO_ACTIVITY, range, dest); + case CPUMF_CTR_SET_EXT: + return stcctm(EXTENDED, range, dest); + case CPUMF_CTR_SET_MT_DIAG: + return stcctm(MT_DIAG_CLEARING, range, dest); + case CPUMF_CTR_SET_MAX: + return 3; + } + return 3; +} + +struct cpu_cf_events { + struct cpumf_ctr_info info; + atomic_t ctr_set[CPUMF_CTR_SET_MAX]; + u64 state; /* For perf_event_open SVC */ + u64 dev_state; /* For /dev/hwctr */ + unsigned int flags; + size_t used; /* Bytes used in data */ + size_t usedss; /* Bytes used in start/stop */ + unsigned char start[PAGE_SIZE]; /* Counter set at event add */ + unsigned char stop[PAGE_SIZE]; /* Counter set at event delete */ + unsigned char data[PAGE_SIZE]; /* Counter set at /dev/hwctr */ + unsigned int sets; /* # Counter set saved in memory */ +}; + +/* Per-CPU event structure for the counter facility */ +static DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events); + static unsigned int cfdiag_cpu_speed; /* CPU speed for CF_DIAG trailer */ static debug_info_t *cf_dbg; @@ -435,6 +506,51 @@ static void cpumf_pmu_disable(struct pmu *pmu) cpuhw->flags &= ~PMU_F_ENABLED; } +#define PMC_INIT 0 +#define PMC_RELEASE 1 + +static void cpum_cf_setup_cpu(void *flags) +{ + struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events); + + switch (*((int *)flags)) { + case PMC_INIT: + memset(&cpuhw->info, 0, sizeof(cpuhw->info)); + qctri(&cpuhw->info); + cpuhw->flags |= PMU_F_RESERVED; + break; + + case PMC_RELEASE: + cpuhw->flags &= ~PMU_F_RESERVED; + break; + } + + /* Disable CPU counter sets */ + lcctl(0); + debug_sprintf_event(cf_dbg, 5, "%s flags %#x flags %#x state %#llx\n", + __func__, *(int *)flags, cpuhw->flags, + cpuhw->state); +} + +/* Initialize the CPU-measurement counter facility */ +static int __kernel_cpumcf_begin(void) +{ + int flags = PMC_INIT; + + on_each_cpu(cpum_cf_setup_cpu, &flags, 1); + irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); + + return 0; +} + +/* Release the CPU-measurement counter facility */ +static void __kernel_cpumcf_end(void) +{ + int flags = PMC_RELEASE; + + on_each_cpu(cpum_cf_setup_cpu, &flags, 1); + irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); +} /* Number of perf events counting hardware events */ static atomic_t num_events = ATOMIC_INIT(0); @@ -821,12 +937,70 @@ static struct pmu cpumf_pmu = { .read = cpumf_pmu_read, }; +static int cpum_cf_setup(unsigned int cpu, int flags) +{ + local_irq_disable(); + cpum_cf_setup_cpu(&flags); + local_irq_enable(); + return 0; +} + +static int cfset_online_cpu(unsigned int cpu); +static int cpum_cf_online_cpu(unsigned int cpu) +{ + debug_sprintf_event(cf_dbg, 4, "%s cpu %d in_irq %ld\n", __func__, + cpu, in_interrupt()); + cpum_cf_setup(cpu, PMC_INIT); + return cfset_online_cpu(cpu); +} + +static int cfset_offline_cpu(unsigned int cpu); +static int cpum_cf_offline_cpu(unsigned int cpu) +{ + debug_sprintf_event(cf_dbg, 4, "%s cpu %d\n", __func__, cpu); + cfset_offline_cpu(cpu); + return cpum_cf_setup(cpu, PMC_RELEASE); +} + /* Return true if store counter set multiple instruction is available */ static inline int stccm_avail(void) { return test_facility(142); } +/* CPU-measurement alerts for the counter facility */ +static void cpumf_measurement_alert(struct ext_code ext_code, + unsigned int alert, unsigned long unused) +{ + struct cpu_cf_events *cpuhw; + + if (!(alert & CPU_MF_INT_CF_MASK)) + return; + + inc_irq_stat(IRQEXT_CMC); + cpuhw = this_cpu_ptr(&cpu_cf_events); + + /* + * Measurement alerts are shared and might happen when the PMU + * is not reserved. Ignore these alerts in this case. + */ + if (!(cpuhw->flags & PMU_F_RESERVED)) + return; + + /* counter authorization change alert */ + if (alert & CPU_MF_INT_CF_CACA) + qctri(&cpuhw->info); + + /* loss of counter data alert */ + if (alert & CPU_MF_INT_CF_LCDA) + pr_err("CPU[%i] Counter data was lost\n", smp_processor_id()); + + /* loss of MT counter data alert */ + if (alert & CPU_MF_INT_CF_MTDA) + pr_warn("CPU[%i] MT counter data was lost\n", + smp_processor_id()); +} + static int cfset_init(void); static int __init cpumf_pmu_init(void) { @@ -835,23 +1009,48 @@ static int __init cpumf_pmu_init(void) if (!cpum_cf_avail()) return -ENODEV; + /* + * Clear bit 15 of cr0 to unauthorize problem-state to + * extract measurement counters + */ + ctl_clear_bit(0, 48); + + /* register handler for measurement-alert interruptions */ + rc = register_external_irq(EXT_IRQ_MEASURE_ALERT, + cpumf_measurement_alert); + if (rc) { + pr_err("Registering for CPU-measurement alerts failed with rc=%i\n", rc); + return rc; + } + /* Setup s390dbf facility */ cf_dbg = debug_register(KMSG_COMPONENT, 2, 1, 128); if (!cf_dbg) { pr_err("Registration of s390dbf(cpum_cf) failed\n"); - return -ENOMEM; + rc = -ENOMEM; + goto out1; } debug_register_view(cf_dbg, &debug_sprintf_view); cpumf_pmu.attr_groups = cpumf_cf_event_group(); rc = perf_pmu_register(&cpumf_pmu, "cpum_cf", -1); if (rc) { - debug_unregister_view(cf_dbg, &debug_sprintf_view); - debug_unregister(cf_dbg); pr_err("Registering the cpum_cf PMU failed with rc=%i\n", rc); + goto out2; } else if (stccm_avail()) { /* Setup counter set device */ cfset_init(); } + + rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE, + "perf/s390/cf:online", + cpum_cf_online_cpu, cpum_cf_offline_cpu); + return rc; + +out2: + debug_unregister_view(cf_dbg, &debug_sprintf_view); + debug_unregister(cf_dbg); +out1: + unregister_external_irq(EXT_IRQ_MEASURE_ALERT, cpumf_measurement_alert); return rc; } @@ -1069,7 +1268,6 @@ static int cfset_all_start(struct cfset_request *req) return rc; } - /* Return the maximum required space for all possible CPUs in case one * CPU will be onlined during the START, READ, STOP cycles. * To find out the size of the counter sets, any one CPU will do. They @@ -1332,7 +1530,7 @@ static struct miscdevice cfset_dev = { /* Hotplug add of a CPU. Scan through all active processes and add * that CPU to the list of CPUs supplied with ioctl(..., START, ...). */ -int cfset_online_cpu(unsigned int cpu) +static int cfset_online_cpu(unsigned int cpu) { struct cfset_call_on_cpu_parm p; struct cfset_request *rp; @@ -1352,7 +1550,7 @@ int cfset_online_cpu(unsigned int cpu) /* Hotplug remove of a CPU. Scan through all active processes and clear * that CPU from the list of CPUs supplied with ioctl(..., START, ...). */ -int cfset_offline_cpu(unsigned int cpu) +static int cfset_offline_cpu(unsigned int cpu) { struct cfset_call_on_cpu_parm p; struct cfset_request *rp; diff --git a/arch/s390/kernel/perf_cpum_cf_common.c b/arch/s390/kernel/perf_cpum_cf_common.c deleted file mode 100644 index 99523dc3fc8b..000000000000 --- a/arch/s390/kernel/perf_cpum_cf_common.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * CPU-Measurement Counter Facility Support - Common Layer - * - * Copyright IBM Corp. 2019 - * Author(s): Hendrik Brueckner - */ -#define KMSG_COMPONENT "cpum_cf_common" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Per-CPU event structure for the counter facility */ -DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events) = { - .ctr_set = { - [CPUMF_CTR_SET_BASIC] = ATOMIC_INIT(0), - [CPUMF_CTR_SET_USER] = ATOMIC_INIT(0), - [CPUMF_CTR_SET_CRYPTO] = ATOMIC_INIT(0), - [CPUMF_CTR_SET_EXT] = ATOMIC_INIT(0), - [CPUMF_CTR_SET_MT_DIAG] = ATOMIC_INIT(0), - }, - .state = 0, - .dev_state = 0, - .flags = 0, - .used = 0, - .usedss = 0, - .sets = 0 -}; -/* Indicator whether the CPU-Measurement Counter Facility Support is ready */ -static bool cpum_cf_initalized; - -/* CPU-measurement alerts for the counter facility */ -static void cpumf_measurement_alert(struct ext_code ext_code, - unsigned int alert, unsigned long unused) -{ - struct cpu_cf_events *cpuhw; - - if (!(alert & CPU_MF_INT_CF_MASK)) - return; - - inc_irq_stat(IRQEXT_CMC); - cpuhw = this_cpu_ptr(&cpu_cf_events); - - /* Measurement alerts are shared and might happen when the PMU - * is not reserved. Ignore these alerts in this case. */ - if (!(cpuhw->flags & PMU_F_RESERVED)) - return; - - /* counter authorization change alert */ - if (alert & CPU_MF_INT_CF_CACA) - qctri(&cpuhw->info); - - /* loss of counter data alert */ - if (alert & CPU_MF_INT_CF_LCDA) - pr_err("CPU[%i] Counter data was lost\n", smp_processor_id()); - - /* loss of MT counter data alert */ - if (alert & CPU_MF_INT_CF_MTDA) - pr_warn("CPU[%i] MT counter data was lost\n", - smp_processor_id()); -} - -#define PMC_INIT 0 -#define PMC_RELEASE 1 -static void cpum_cf_setup_cpu(void *flags) -{ - struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events); - - switch (*((int *) flags)) { - case PMC_INIT: - memset(&cpuhw->info, 0, sizeof(cpuhw->info)); - qctri(&cpuhw->info); - cpuhw->flags |= PMU_F_RESERVED; - break; - - case PMC_RELEASE: - cpuhw->flags &= ~PMU_F_RESERVED; - break; - } - - /* Disable CPU counter sets */ - lcctl(0); -} - -/* Initialize the CPU-measurement counter facility */ -int __kernel_cpumcf_begin(void) -{ - int flags = PMC_INIT; - - on_each_cpu(cpum_cf_setup_cpu, &flags, 1); - irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); - - return 0; -} -EXPORT_SYMBOL(__kernel_cpumcf_begin); - -/* Release the CPU-measurement counter facility */ -void __kernel_cpumcf_end(void) -{ - int flags = PMC_RELEASE; - - on_each_cpu(cpum_cf_setup_cpu, &flags, 1); - irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); -} -EXPORT_SYMBOL(__kernel_cpumcf_end); - -static int cpum_cf_setup(unsigned int cpu, int flags) -{ - local_irq_disable(); - cpum_cf_setup_cpu(&flags); - local_irq_enable(); - return 0; -} - -static int cpum_cf_online_cpu(unsigned int cpu) -{ - cpum_cf_setup(cpu, PMC_INIT); - return cfset_online_cpu(cpu); -} - -static int cpum_cf_offline_cpu(unsigned int cpu) -{ - cfset_offline_cpu(cpu); - return cpum_cf_setup(cpu, PMC_RELEASE); -} - -static int __init cpum_cf_init(void) -{ - int rc; - - if (!cpum_cf_avail()) - return -ENODEV; - - /* clear bit 15 of cr0 to unauthorize problem-state to - * extract measurement counters */ - ctl_clear_bit(0, 48); - - /* register handler for measurement-alert interruptions */ - rc = register_external_irq(EXT_IRQ_MEASURE_ALERT, - cpumf_measurement_alert); - if (rc) { - pr_err("Registering for CPU-measurement alerts " - "failed with rc=%i\n", rc); - return rc; - } - - rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE, - "perf/s390/cf:online", - cpum_cf_online_cpu, cpum_cf_offline_cpu); - if (!rc) - cpum_cf_initalized = true; - - return rc; -} -early_initcall(cpum_cf_init); diff --git a/arch/s390/kernel/perf_pai_ext.c b/arch/s390/kernel/perf_pai_ext.c index 1138f57baae3..bc3e49ebf930 100644 --- a/arch/s390/kernel/perf_pai_ext.c +++ b/arch/s390/kernel/perf_pai_ext.c @@ -16,8 +16,8 @@ #include #include #include +#include -#include #include #include #include From 0d5f0dc83073cd36d1e92bbcbcc3bc046918bc69 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 24 Jan 2023 12:20:55 +0100 Subject: [PATCH 124/182] s390/cpum_cf: simplify PMC_INIT and PMC_RELEASE usage Simplify the use of constants PMC_INIT and PMC_RELEASE. Suggested-by: Heiko Carstens Signed-off-by: Thomas Richter Signed-off-by: Heiko Carstens --- arch/s390/kernel/perf_cpum_cf.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 940c717e6531..0cbca3165496 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -506,14 +506,14 @@ static void cpumf_pmu_disable(struct pmu *pmu) cpuhw->flags &= ~PMU_F_ENABLED; } -#define PMC_INIT 0 -#define PMC_RELEASE 1 +#define PMC_INIT 0UL +#define PMC_RELEASE 1UL static void cpum_cf_setup_cpu(void *flags) { struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events); - switch (*((int *)flags)) { + switch ((unsigned long)flags) { case PMC_INIT: memset(&cpuhw->info, 0, sizeof(cpuhw->info)); qctri(&cpuhw->info); @@ -535,9 +535,7 @@ static void cpum_cf_setup_cpu(void *flags) /* Initialize the CPU-measurement counter facility */ static int __kernel_cpumcf_begin(void) { - int flags = PMC_INIT; - - on_each_cpu(cpum_cf_setup_cpu, &flags, 1); + on_each_cpu(cpum_cf_setup_cpu, (void *)PMC_INIT, 1); irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); return 0; @@ -546,9 +544,7 @@ static int __kernel_cpumcf_begin(void) /* Release the CPU-measurement counter facility */ static void __kernel_cpumcf_end(void) { - int flags = PMC_RELEASE; - - on_each_cpu(cpum_cf_setup_cpu, &flags, 1); + on_each_cpu(cpum_cf_setup_cpu, (void *)PMC_RELEASE, 1); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); } @@ -937,10 +933,10 @@ static struct pmu cpumf_pmu = { .read = cpumf_pmu_read, }; -static int cpum_cf_setup(unsigned int cpu, int flags) +static int cpum_cf_setup(unsigned int cpu, unsigned long flags) { local_irq_disable(); - cpum_cf_setup_cpu(&flags); + cpum_cf_setup_cpu((void *)flags); local_irq_enable(); return 0; } From e9c9cb90e76ffaabcc7ca8f275d9e82195fd6367 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Mon, 23 Jan 2023 22:50:32 +0100 Subject: [PATCH 125/182] s390: discard .interp section When debugging vmlinux with QEMU + GDB, the following GDB error may occur: (gdb) c Continuing. Warning: Cannot insert breakpoint -1. Cannot access memory at address 0xffffffffffff95c0 Command aborted. (gdb) The reason is that, when .interp section is present, GDB tries to locate the file specified in it in memory and put a number of breakpoints there (see enable_break() function in gdb/solib-svr4.c). Sometimes GDB finds a bogus location that matches its heuristics, fails to set a breakpoint and stops. This makes further debugging impossible. The .interp section contains misleading information anyway (vmlinux does not need ld.so), so fix by discarding it. Signed-off-by: Ilya Leoshkevich Cc: Signed-off-by: Heiko Carstens --- arch/s390/kernel/vmlinux.lds.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 5c2224f5b128..acdf1e8b2536 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -231,5 +231,6 @@ SECTIONS DISCARDS /DISCARD/ : { *(.eh_frame) + *(.interp) } } From 7be215ba35dbeecdcb502f15686b1430184df43a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 23 Jan 2023 14:30:42 +0100 Subject: [PATCH 126/182] s390/syscalls: remove SYSCALL_METADATA() from compat syscalls SYSCALL_METADATA() is only supposed to be used for non-compat system calls. Otherwise there would be a name clash. This also removes the inconsistency that s390 is the only architecture which uses SYSCALL_METADATA() for compat system calls, and even that only for compat system calls without parameters. Only two such compat system calls exist. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/syscall_wrapper.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/s390/include/asm/syscall_wrapper.h b/arch/s390/include/asm/syscall_wrapper.h index fde7e6b1df48..762438c32f03 100644 --- a/arch/s390/include/asm/syscall_wrapper.h +++ b/arch/s390/include/asm/syscall_wrapper.h @@ -72,7 +72,6 @@ * named __s390x_sys_*() */ #define COMPAT_SYSCALL_DEFINE0(sname) \ - SYSCALL_METADATA(_##sname, 0); \ long __s390_compat_sys_##sname(void); \ ALLOW_ERROR_INJECTION(__s390_compat_sys_##sname, ERRNO); \ long __s390_compat_sys_##sname(void) From 82c1b3e7e5ff4df9b151a61910a244746a631910 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 23 Jan 2023 14:30:43 +0100 Subject: [PATCH 127/182] s390/syscalls: remove __SC_COMPAT_TYPE define Remove __SC_COMPAT_TYPE define which is an unused leftover. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/syscall_wrapper.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/s390/include/asm/syscall_wrapper.h b/arch/s390/include/asm/syscall_wrapper.h index 762438c32f03..de719f18a548 100644 --- a/arch/s390/include/asm/syscall_wrapper.h +++ b/arch/s390/include/asm/syscall_wrapper.h @@ -35,8 +35,6 @@ #define SYSCALL_PT_ARGS(x, ...) SYSCALL_PT_ARG##x(__VA_ARGS__) #ifdef CONFIG_COMPAT -#define __SC_COMPAT_TYPE(t, a) \ - __typeof(__builtin_choose_expr(sizeof(t) > 4, 0L, (t)0)) a #define __SC_COMPAT_CAST(t, a) \ ({ \ From 2e4532d4ac0e9675769258a85030b3ec89708af2 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 23 Jan 2023 14:30:44 +0100 Subject: [PATCH 128/182] s390/syscalls: move __S390_SYS_STUBx() macro Move __S390_SYS_STUBx() the end of the CONFIG_COMPAT section, so both variants (compat and non-compat) are close together and can be easily compared. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/syscall_wrapper.h | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/s390/include/asm/syscall_wrapper.h b/arch/s390/include/asm/syscall_wrapper.h index de719f18a548..64e2dd103080 100644 --- a/arch/s390/include/asm/syscall_wrapper.h +++ b/arch/s390/include/asm/syscall_wrapper.h @@ -54,17 +54,6 @@ (t)__ReS; \ }) -#define __S390_SYS_STUBx(x, name, ...) \ - long __s390_sys##name(struct pt_regs *regs); \ - ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \ - long __s390_sys##name(struct pt_regs *regs) \ - { \ - long ret = __do_sys##name(SYSCALL_PT_ARGS(x, regs, \ - __SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ - __MAP(x,__SC_TEST,__VA_ARGS__); \ - return ret; \ - } - /* * To keep the naming coherent, re-define SYSCALL_DEFINE0 to create an alias * named __s390x_sys_*() @@ -121,9 +110,18 @@ #define COMPAT_SYS_NI(name) \ SYSCALL_ALIAS(__s390_compat_sys_##name, sys_ni_posix_timers) -#else /* CONFIG_COMPAT */ +#define __S390_SYS_STUBx(x, name, ...) \ + long __s390_sys##name(struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \ + long __s390_sys##name(struct pt_regs *regs) \ + { \ + long ret = __do_sys##name(SYSCALL_PT_ARGS(x, regs, \ + __SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + return ret; \ + } -#define __S390_SYS_STUBx(x, fullname, name, ...) +#else /* CONFIG_COMPAT */ #define SYSCALL_DEFINE0(sname) \ SYSCALL_METADATA(_##sname, 0); \ @@ -137,6 +135,8 @@ #define SYS_NI(name) \ SYSCALL_ALIAS(__s390x_sys_##name, sys_ni_posix_timers); +#define __S390_SYS_STUBx(x, fullname, name, ...) + #endif /* CONFIG_COMPAT */ #define __SYSCALL_DEFINEx(x, name, ...) \ From 0efc5d58bd28260b42d934e95092e26bdf2a4724 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 23 Jan 2023 14:30:45 +0100 Subject: [PATCH 129/182] s390/syscalls: remove trailing semicolon Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/syscall_wrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/syscall_wrapper.h b/arch/s390/include/asm/syscall_wrapper.h index 64e2dd103080..8007d9ebdab4 100644 --- a/arch/s390/include/asm/syscall_wrapper.h +++ b/arch/s390/include/asm/syscall_wrapper.h @@ -133,7 +133,7 @@ cond_syscall(__s390x_sys_##name) #define SYS_NI(name) \ - SYSCALL_ALIAS(__s390x_sys_##name, sys_ni_posix_timers); + SYSCALL_ALIAS(__s390x_sys_##name, sys_ni_posix_timers) #define __S390_SYS_STUBx(x, fullname, name, ...) From 2213d44e140f979f4b60c3c0f8dd56d151cc8692 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 23 Jan 2023 14:30:46 +0100 Subject: [PATCH 130/182] s390/syscalls: get rid of system call alias functions bpftrace and friends only consider functions present in /sys/kernel/tracing/available_filter_functions. For system calls there is the s390 specific problem that the system call function itself is present via __se_sys##name() while the system call itself is wired up via an __s390x_sys##name() alias. The required DWARF debug information however is only available for the original function, not the alias, but within available_filter_functions only the functions with __s390x_ prefix are available. Which means the required DWARF debug information cannot be found. While this could be solved via tooling, it is easier to change the s390 specific system call wrapper handling. Therefore get rid of this alias handling and implement system call wrappers like most other architectures are doing. In result the implementation generates the following functions: long __s390x_sys##name(struct pt_regs *regs) static inline long __se_sys##name(...) static inline long __do_sys##name(...) __s390x_sys##name() is the visible system call function which is also wired up in the system call table. Its only parameter is a pt_regs variable. This function calls the corresponding __se_sys##name() function, which has as many parameters like the system call definition. This function in turn performs all zero and sign extensions of all system call parameters, taken from the pt_regs structure, and finally calls __do_sys##name(). __do_sys##name() is the actual inlined system call function implementation. For all 64 bit system calls there is a 31/32 bit system call function __s390_sys##name() generated, which handles all system call parameters correctly as required by compat handling. This function may be wired up within the compat system call table, unless there exists an explicit compat system call function, which is then used instead. Reported-by: Ilya Leoshkevich Tested-by: Ilya Leoshkevich Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/syscall_wrapper.h | 121 +++++++++++------------- 1 file changed, 55 insertions(+), 66 deletions(-) diff --git a/arch/s390/include/asm/syscall_wrapper.h b/arch/s390/include/asm/syscall_wrapper.h index 8007d9ebdab4..9286430fe729 100644 --- a/arch/s390/include/asm/syscall_wrapper.h +++ b/arch/s390/include/asm/syscall_wrapper.h @@ -7,32 +7,11 @@ #ifndef _ASM_S390_SYSCALL_WRAPPER_H #define _ASM_S390_SYSCALL_WRAPPER_H -#define __SC_TYPE(t, a) t - -#define SYSCALL_PT_ARG6(regs, m, t1, t2, t3, t4, t5, t6)\ - SYSCALL_PT_ARG5(regs, m, t1, t2, t3, t4, t5), \ - m(t6, (regs->gprs[7])) - -#define SYSCALL_PT_ARG5(regs, m, t1, t2, t3, t4, t5) \ - SYSCALL_PT_ARG4(regs, m, t1, t2, t3, t4), \ - m(t5, (regs->gprs[6])) - -#define SYSCALL_PT_ARG4(regs, m, t1, t2, t3, t4) \ - SYSCALL_PT_ARG3(regs, m, t1, t2, t3), \ - m(t4, (regs->gprs[5])) - -#define SYSCALL_PT_ARG3(regs, m, t1, t2, t3) \ - SYSCALL_PT_ARG2(regs, m, t1, t2), \ - m(t3, (regs->gprs[4])) - -#define SYSCALL_PT_ARG2(regs, m, t1, t2) \ - SYSCALL_PT_ARG1(regs, m, t1), \ - m(t2, (regs->gprs[3])) - -#define SYSCALL_PT_ARG1(regs, m, t1) \ - m(t1, (regs->orig_gpr2)) - -#define SYSCALL_PT_ARGS(x, ...) SYSCALL_PT_ARG##x(__VA_ARGS__) +/* Mapping of registers to parameters for syscalls */ +#define SC_S390_REGS_TO_ARGS(x, ...) \ + __MAP(x, __SC_ARGS \ + ,, regs->orig_gpr2,, regs->gprs[3],, regs->gprs[4] \ + ,, regs->gprs[5],, regs->gprs[6],, regs->gprs[7]) #ifdef CONFIG_COMPAT @@ -65,11 +44,20 @@ #define SYSCALL_DEFINE0(sname) \ SYSCALL_METADATA(_##sname, 0); \ + long __s390_sys_##sname(void); \ + ALLOW_ERROR_INJECTION(__s390_sys_##sname, ERRNO); \ long __s390x_sys_##sname(void); \ ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \ + static inline long __do_sys_##sname(void); \ long __s390_sys_##sname(void) \ - __attribute__((alias(__stringify(__s390x_sys_##sname)))); \ - long __s390x_sys_##sname(void) + { \ + return __do_sys_##sname(); \ + } \ + long __s390x_sys_##sname(void) \ + { \ + return __do_sys_##sname(); \ + } \ + static inline long __do_sys_##sname(void) #define COND_SYSCALL(name) \ cond_syscall(__s390x_sys_##name); \ @@ -80,24 +68,20 @@ SYSCALL_ALIAS(__s390_sys_##name, sys_ni_posix_timers) #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ - __diag_push(); \ - __diag_ignore(GCC, 8, "-Wattribute-alias", \ - "Type aliasing is used to sanitize syscall arguments"); \ long __s390_compat_sys##name(struct pt_regs *regs); \ - long __s390_compat_sys##name(struct pt_regs *regs) \ - __attribute__((alias(__stringify(__se_compat_sys##name)))); \ ALLOW_ERROR_INJECTION(__s390_compat_sys##name, ERRNO); \ - static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ - long __se_compat_sys##name(struct pt_regs *regs); \ - long __se_compat_sys##name(struct pt_regs *regs) \ + static inline long __se_compat_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)); \ + static inline long __do_compat_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)); \ + long __s390_compat_sys##name(struct pt_regs *regs) \ { \ - long ret = __do_compat_sys##name(SYSCALL_PT_ARGS(x, regs, __SC_DELOUSE, \ - __MAP(x, __SC_TYPE, __VA_ARGS__))); \ - __MAP(x,__SC_TEST,__VA_ARGS__); \ - return ret; \ + return __se_compat_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__)); \ } \ - __diag_pop(); \ - static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + static inline long __se_compat_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)) \ + { \ + __MAP(x, __SC_TEST, __VA_ARGS__); \ + return __do_compat_sys##name(__MAP(x, __SC_DELOUSE, __VA_ARGS__)); \ + } \ + static inline long __do_compat_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)) /* * As some compat syscalls may not be implemented, we need to expand @@ -113,12 +97,15 @@ #define __S390_SYS_STUBx(x, name, ...) \ long __s390_sys##name(struct pt_regs *regs); \ ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \ + static inline long ___se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)); \ long __s390_sys##name(struct pt_regs *regs) \ { \ - long ret = __do_sys##name(SYSCALL_PT_ARGS(x, regs, \ - __SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ - __MAP(x,__SC_TEST,__VA_ARGS__); \ - return ret; \ + return ___se_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__)); \ + } \ + static inline long ___se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)) \ + { \ + __MAP(x, __SC_TEST, __VA_ARGS__); \ + return __do_sys##name(__MAP(x, __SC_COMPAT_CAST, __VA_ARGS__)); \ } #else /* CONFIG_COMPAT */ @@ -127,7 +114,12 @@ SYSCALL_METADATA(_##sname, 0); \ long __s390x_sys_##sname(void); \ ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \ - long __s390x_sys_##sname(void) + static inline long __do_sys_##sname(void); \ + long __s390x_sys_##sname(void) \ + { \ + return __do_sys_##sname(); \ + } \ + static inline long __do_sys_##sname(void) #define COND_SYSCALL(name) \ cond_syscall(__s390x_sys_##name) @@ -139,24 +131,21 @@ #endif /* CONFIG_COMPAT */ -#define __SYSCALL_DEFINEx(x, name, ...) \ - __diag_push(); \ - __diag_ignore(GCC, 8, "-Wattribute-alias", \ - "Type aliasing is used to sanitize syscall arguments"); \ - long __s390x_sys##name(struct pt_regs *regs) \ - __attribute__((alias(__stringify(__se_sys##name)))); \ - ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \ - static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ - long __se_sys##name(struct pt_regs *regs); \ - __S390_SYS_STUBx(x, name, __VA_ARGS__) \ - long __se_sys##name(struct pt_regs *regs) \ - { \ - long ret = __do_sys##name(SYSCALL_PT_ARGS(x, regs, \ - __SC_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \ - __MAP(x,__SC_TEST,__VA_ARGS__); \ - return ret; \ - } \ - __diag_pop(); \ - static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) +#define __SYSCALL_DEFINEx(x, name, ...) \ + long __s390x_sys##name(struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \ + static inline long __se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)); \ + static inline long __do_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)); \ + __S390_SYS_STUBx(x, name, __VA_ARGS__); \ + long __s390x_sys##name(struct pt_regs *regs) \ + { \ + return __se_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__)); \ + } \ + static inline long __se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)) \ + { \ + __MAP(x, __SC_TEST, __VA_ARGS__); \ + return __do_sys##name(__MAP(x, __SC_CAST, __VA_ARGS__)); \ + } \ + static inline long __do_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)) #endif /* _ASM_S390_SYSCALL_WRAPPER_H */ From e966ccf836e8964edf984adc4b4af5f3a3e07de6 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Mon, 23 Jan 2023 15:24:17 +0100 Subject: [PATCH 131/182] s390/boot: avoid mapping standby memory Commit bb1520d581a3 ("s390/mm: start kernel with DAT enabled") doesn't consider online memory holes due to potential memory offlining and erroneously creates pgtables for stand-by memory, which bear RW+X attribute and trigger a warning: RANGE SIZE STATE REMOVABLE BLOCK 0x0000000000000000-0x0000000c3fffffff 49G online yes 0-48 0x0000000c40000000-0x0000000c7fffffff 1G offline 49 0x0000000c80000000-0x0000000fffffffff 14G online yes 50-63 0x0000001000000000-0x00000013ffffffff 16G offline 64-79 s390/mm: Found insecure W+X mapping at address 0xc40000000 WARNING: CPU: 14 PID: 1 at arch/s390/mm/dump_pagetables.c:142 note_page+0x2cc/0x2d8 Map only online memory ranges which fit within identity mapping limit. Fixes: bb1520d581a3 ("s390/mm: start kernel with DAT enabled") Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 2 +- arch/s390/boot/startup.c | 4 +--- arch/s390/boot/vmem.c | 16 +++++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 8abedae76e53..830cfabaa6a0 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -44,7 +44,7 @@ void print_missing_facilities(void); void sclp_early_setup_buffer(void); void print_pgm_check_info(void); unsigned long get_random_base(unsigned long safe_addr); -void setup_vmem(unsigned long online_end, unsigned long asce_limit); +void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit); void __printf(1, 2) decompressor_printk(const char *fmt, ...); void error(char *m); diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index cb4b743a5e17..c9dfd7e09233 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -278,7 +278,6 @@ void startup_kernel(void) unsigned long random_lma; unsigned long safe_addr; unsigned long asce_limit; - unsigned long online_end; void *img; psw_t psw; @@ -303,7 +302,6 @@ void startup_kernel(void) setup_ident_map_size(detect_memory()); setup_vmalloc_size(); asce_limit = setup_kernel_memory_layout(); - online_end = min(get_mem_detect_end(), ident_map_size); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) { random_lma = get_random_base(safe_addr); @@ -335,7 +333,7 @@ void startup_kernel(void) */ clear_bss_section(); handle_relocs(__kaslr_offset); - setup_vmem(online_end, asce_limit); + setup_vmem(ident_map_size, asce_limit); copy_bootdata(); if (__kaslr_offset) { diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index 3bcef4fcea80..edcad545b949 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -39,7 +39,7 @@ static void boot_check_oom(void) error("out of memory on boot\n"); } -static void pgtable_populate_begin(unsigned long online_end) +static void pgtable_populate_begin(unsigned long ident_map_size) { unsigned long initrd_end; unsigned long kernel_end; @@ -51,7 +51,7 @@ static void pgtable_populate_begin(unsigned long online_end) pgalloc_low = max(pgalloc_low, initrd_end); } - pgalloc_end = round_down(online_end, PAGE_SIZE); + pgalloc_end = round_down(min(ident_map_size, get_mem_detect_end()), PAGE_SIZE); pgalloc_pos = pgalloc_end; boot_check_oom(); @@ -247,10 +247,12 @@ static void pgtable_populate_end(void) } while (pgalloc_pos < pgalloc_pos_prev); } -void setup_vmem(unsigned long online_end, unsigned long asce_limit) +void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit) { + unsigned long start, end; unsigned long asce_type; unsigned long asce_bits; + int i; if (asce_limit == _REGION1_SIZE) { asce_type = _REGION2_ENTRY_EMPTY; @@ -272,9 +274,13 @@ void setup_vmem(unsigned long online_end, unsigned long asce_limit) * No further pgtable_populate() calls are allowed after the value * of pgalloc_pos finalized with a call to pgtable_populate_end(). */ - pgtable_populate_begin(online_end); + pgtable_populate_begin(ident_map_size); pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); - pgtable_populate(0, online_end, POPULATE_ONE2ONE); + for_each_mem_detect_block(i, &start, &end) { + if (start >= ident_map_size) + break; + pgtable_populate(start, min(end, ident_map_size), POPULATE_ONE2ONE); + } pgtable_populate(__abs_lowcore, __abs_lowcore + sizeof(struct lowcore), POPULATE_ABS_LOWCORE); pgtable_populate(__memcpy_real_area, __memcpy_real_area + PAGE_SIZE, From 39da9a979c4f5b07862289146b9db38209b92634 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 24 Jan 2023 17:03:24 +0100 Subject: [PATCH 132/182] s390/boot: remove pgtable_populate_end setup_vmem() already calls populate for all online memory regions. pgtable_populate_end() could be removed. Also rename pgtable_populate_begin() to pgtable_populate_init(). Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/vmem.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index edcad545b949..a35c251c9123 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -39,7 +39,7 @@ static void boot_check_oom(void) error("out of memory on boot\n"); } -static void pgtable_populate_begin(unsigned long ident_map_size) +static void pgtable_populate_init(unsigned long ident_map_size) { unsigned long initrd_end; unsigned long kernel_end; @@ -226,27 +226,6 @@ static void pgtable_populate(unsigned long addr, unsigned long end, enum populat } } -/* - * The pgtables are located in the range [pgalloc_pos, pgalloc_end). - * That range must stay intact and is later reserved in the memblock. - * Therefore pgtable_populate(pgalloc_pos, pgalloc_end) is needed to - * finalize pgalloc_pos pointer. However that call can decrease the - * value of pgalloc_pos pointer itself. Therefore, pgtable_populate() - * needs to be called repeatedly until pgtables are complete and - * pgalloc_pos does not grow left anymore. - */ -static void pgtable_populate_end(void) -{ - unsigned long pgalloc_end_curr = pgalloc_end; - unsigned long pgalloc_pos_prev; - - do { - pgalloc_pos_prev = pgalloc_pos; - pgtable_populate(pgalloc_pos, pgalloc_end_curr, POPULATE_ONE2ONE); - pgalloc_end_curr = pgalloc_pos_prev; - } while (pgalloc_pos < pgalloc_pos_prev); -} - void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit) { unsigned long start, end; @@ -270,11 +249,8 @@ void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit) * To allow prefixing the lowcore must be mapped with 4KB pages. * To prevent creation of a large page at address 0 first map * the lowcore and create the identity mapping only afterwards. - * - * No further pgtable_populate() calls are allowed after the value - * of pgalloc_pos finalized with a call to pgtable_populate_end(). */ - pgtable_populate_begin(ident_map_size); + pgtable_populate_init(ident_map_size); pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); for_each_mem_detect_block(i, &start, &end) { if (start >= ident_map_size) @@ -286,7 +262,6 @@ void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit) pgtable_populate(__memcpy_real_area, __memcpy_real_area + PAGE_SIZE, POPULATE_NONE); memcpy_real_ptep = __virt_to_kpte(__memcpy_real_area); - pgtable_populate_end(); S390_lowcore.kernel_asce = swapper_pg_dir | asce_bits; S390_lowcore.user_asce = s390_invalid_asce; From 05178996e1a77e2a4664536e6d101a086a905034 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 24 Jan 2023 18:08:38 +0100 Subject: [PATCH 133/182] s390/mm,ptdump: avoid Kasan vs Memcpy Real markers swapping ---[ Real Memory Copy Area Start ]--- 0x001bfffffffff000-0x001c000000000000 4K PTE I ---[ Kasan Shadow Start ]--- ---[ Real Memory Copy Area End ]--- 0x001c000000000000-0x001c000200000000 8G PMD RW NX ... ---[ Kasan Shadow End ]--- ptdump does a stable sort of markers. Move kasan markers after memcpy real to avoid swapping. Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/mm/dump_pagetables.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index 9953819d7959..ba5f80268878 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c @@ -33,10 +33,6 @@ enum address_markers_idx { #endif IDENTITY_AFTER_NR, IDENTITY_AFTER_END_NR, -#ifdef CONFIG_KASAN - KASAN_SHADOW_START_NR, - KASAN_SHADOW_END_NR, -#endif VMEMMAP_NR, VMEMMAP_END_NR, VMALLOC_NR, @@ -47,6 +43,10 @@ enum address_markers_idx { ABS_LOWCORE_END_NR, MEMCPY_REAL_NR, MEMCPY_REAL_END_NR, +#ifdef CONFIG_KASAN + KASAN_SHADOW_START_NR, + KASAN_SHADOW_END_NR, +#endif }; static struct addr_marker address_markers[] = { @@ -62,10 +62,6 @@ static struct addr_marker address_markers[] = { #endif [IDENTITY_AFTER_NR] = {(unsigned long)_end, "Identity Mapping Start"}, [IDENTITY_AFTER_END_NR] = {0, "Identity Mapping End"}, -#ifdef CONFIG_KASAN - [KASAN_SHADOW_START_NR] = {KASAN_SHADOW_START, "Kasan Shadow Start"}, - [KASAN_SHADOW_END_NR] = {KASAN_SHADOW_END, "Kasan Shadow End"}, -#endif [VMEMMAP_NR] = {0, "vmemmap Area Start"}, [VMEMMAP_END_NR] = {0, "vmemmap Area End"}, [VMALLOC_NR] = {0, "vmalloc Area Start"}, @@ -76,6 +72,10 @@ static struct addr_marker address_markers[] = { [ABS_LOWCORE_END_NR] = {0, "Lowcore Area End"}, [MEMCPY_REAL_NR] = {0, "Real Memory Copy Area Start"}, [MEMCPY_REAL_END_NR] = {0, "Real Memory Copy Area End"}, +#ifdef CONFIG_KASAN + [KASAN_SHADOW_START_NR] = {KASAN_SHADOW_START, "Kasan Shadow Start"}, + [KASAN_SHADOW_END_NR] = {KASAN_SHADOW_END, "Kasan Shadow End"}, +#endif { -1, NULL } }; From 0c6924c262e819c4997b9cae0df6bd6e7b0b8754 Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Fri, 11 Nov 2022 13:46:33 +0100 Subject: [PATCH 134/182] s390/cio: introduce locking for register/unregister functions Unbinding an I/O subchannel with a child-CCW device in disconnected state sometimes causes a kernel-panic. The race condition was seen mostly during testing, when setting all the CHPIDs of a device to offline and at the same time, the unbinding the I/O subchannel driver. The kernel-panic occurs because of double delete, the I/O subchannel driver calls device_del on the CCW device while another device_del invocation for the same device is in-flight. For instance, disabling all the CHPIDs will trigger the ccw_device_remove function, which will call a ccw_device_unregister(), which ends up calling the device_del() which is asynchronous via cdev's todo workqueue. And unbinding the I/O subchannel driver calls io_subchannel_remove() function which calls the ccw_device_unregister() and device_del(). This double delete can be prevented by serializing all CCW device registration/unregistration calls into the driver core. This patch introduces a mutex which will be used for this purpose. Signed-off-by: Vineeth Vijayan Reported-by: Boris Fiuczynski Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ccwdev.h | 2 ++ drivers/s390/cio/device.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index bd1596810cc1..91d261751d25 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -15,6 +15,7 @@ #include #include #include +#include /* structs from asm/cio.h */ struct irb; @@ -87,6 +88,7 @@ struct ccw_device { spinlock_t *ccwlock; /* private: */ struct ccw_device_private *private; /* cio private information */ + struct mutex reg_mutex; /* public: */ struct ccw_device_id id; struct ccw_driver *drv; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 9e0cf44ff9d4..5418e60dbfc3 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -244,10 +244,13 @@ int ccw_device_is_orphan(struct ccw_device *cdev) static void ccw_device_unregister(struct ccw_device *cdev) { + mutex_lock(&cdev->reg_mutex); if (device_is_registered(&cdev->dev)) { /* Undo device_add(). */ device_del(&cdev->dev); } + mutex_unlock(&cdev->reg_mutex); + if (cdev->private->flags.initialized) { cdev->private->flags.initialized = 0; /* Release reference from device_initialize(). */ @@ -653,11 +656,13 @@ static void ccw_device_do_unbind_bind(struct ccw_device *cdev) { int ret; + mutex_lock(&cdev->reg_mutex); if (device_is_registered(&cdev->dev)) { device_release_driver(&cdev->dev); ret = device_attach(&cdev->dev); WARN_ON(ret == -ENODEV); } + mutex_unlock(&cdev->reg_mutex); } static void @@ -740,6 +745,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, INIT_LIST_HEAD(&priv->cmb_list); init_waitqueue_head(&priv->wait_q); timer_setup(&priv->timer, ccw_device_timeout, 0); + mutex_init(&cdev->reg_mutex); atomic_set(&priv->onoff, 0); cdev->ccwlock = sch->lock; @@ -825,6 +831,7 @@ static void io_subchannel_register(struct ccw_device *cdev) * be registered). We need to reprobe since we may now have sense id * information. */ + mutex_lock(&cdev->reg_mutex); if (device_is_registered(&cdev->dev)) { if (!cdev->drv) { ret = device_reprobe(&cdev->dev); @@ -847,12 +854,14 @@ static void io_subchannel_register(struct ccw_device *cdev) spin_lock_irqsave(sch->lock, flags); sch_set_cdev(sch, NULL); spin_unlock_irqrestore(sch->lock, flags); + mutex_unlock(&cdev->reg_mutex); /* Release initial device reference. */ put_device(&cdev->dev); goto out_err; } out: cdev->private->flags.recog_done = 1; + mutex_unlock(&cdev->reg_mutex); wake_up(&cdev->private->wait_q); out_err: if (adjust_init_count && atomic_dec_and_test(&ccw_device_init_count)) From cbc29f107e51b1cc7d1e7b0bbe0691a1224205f1 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 25 Jan 2023 19:16:10 +0100 Subject: [PATCH 135/182] s390/mem_detect: do not update output parameters on failure Function __get_mem_detect_block() resets start and end output parameters in case of invalid mem_detect array index is provided. That violates the rule of sparing the output on fail path and leads e.g to a below anomaly: for_each_mem_detect_block(i, &start, &end) continue; One would expect start and end contain addresses of the last memory block (if available), but in fact the two will be reset to zeroes. That is not how an iterator is expected to work. Reviewed-by: Vasily Gorbik Signed-off-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/include/asm/mem_detect.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/s390/include/asm/mem_detect.h b/arch/s390/include/asm/mem_detect.h index a7c922a69050..058ac2647eb7 100644 --- a/arch/s390/include/asm/mem_detect.h +++ b/arch/s390/include/asm/mem_detect.h @@ -40,11 +40,8 @@ void add_mem_detect_block(u64 start, u64 end); static inline int __get_mem_detect_block(u32 n, unsigned long *start, unsigned long *end) { - if (n >= mem_detect.count) { - *start = 0; - *end = 0; + if (n >= mem_detect.count) return -1; - } if (n < MEM_INLINED_ENTRIES) { *start = (unsigned long)mem_detect.entries[n].start; From c676aac66f5b2b03a1090bc6b1891486255f7159 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 29 Jan 2023 16:36:03 +0100 Subject: [PATCH 136/182] s390/ipl: add DEFINE_GENERIC_LOADPARM() In the current code each reipl type implements its own pair of loadparm show/store functions. Add a macro to deduplicate the code a bit. Reviewed-by: Heiko Carstens Signed-off-by: Sven Schnelle Fixes: 87fd22e0ae92 ("s390/ipl: add eckd support") Cc: Signed-off-by: Heiko Carstens --- arch/s390/kernel/ipl.c | 89 +++++++++--------------------------------- 1 file changed, 19 insertions(+), 70 deletions(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 406766c894fb..d7b433261145 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -888,23 +888,26 @@ static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, return len; } -/* FCP wrapper */ -static ssize_t reipl_fcp_loadparm_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) -{ - return reipl_generic_loadparm_show(reipl_block_fcp, page); -} +#define DEFINE_GENERIC_LOADPARM(name) \ +static ssize_t reipl_##name##_loadparm_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *page) \ +{ \ + return reipl_generic_loadparm_show(reipl_block_##name, page); \ +} \ +static ssize_t reipl_##name##_loadparm_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return reipl_generic_loadparm_store(reipl_block_##name, buf, len); \ +} \ +static struct kobj_attribute sys_reipl_##name##_loadparm_attr = \ + __ATTR(loadparm, 0644, reipl_##name##_loadparm_show, \ + reipl_##name##_loadparm_store) -static ssize_t reipl_fcp_loadparm_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) -{ - return reipl_generic_loadparm_store(reipl_block_fcp, buf, len); -} - -static struct kobj_attribute sys_reipl_fcp_loadparm_attr = - __ATTR(loadparm, 0644, reipl_fcp_loadparm_show, - reipl_fcp_loadparm_store); +DEFINE_GENERIC_LOADPARM(fcp); +DEFINE_GENERIC_LOADPARM(nvme); +DEFINE_GENERIC_LOADPARM(ccw); +DEFINE_GENERIC_LOADPARM(nss); static ssize_t reipl_fcp_clear_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -994,24 +997,6 @@ DEFINE_IPL_ATTR_RW(reipl_nvme, bootprog, "%lld\n", "%lld\n", DEFINE_IPL_ATTR_RW(reipl_nvme, br_lba, "%lld\n", "%lld\n", reipl_block_nvme->nvme.br_lba); -/* nvme wrapper */ -static ssize_t reipl_nvme_loadparm_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) -{ - return reipl_generic_loadparm_show(reipl_block_nvme, page); -} - -static ssize_t reipl_nvme_loadparm_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) -{ - return reipl_generic_loadparm_store(reipl_block_nvme, buf, len); -} - -static struct kobj_attribute sys_reipl_nvme_loadparm_attr = - __ATTR(loadparm, 0644, reipl_nvme_loadparm_show, - reipl_nvme_loadparm_store); - static struct attribute *reipl_nvme_attrs[] = { &sys_reipl_nvme_fid_attr.attr, &sys_reipl_nvme_nsid_attr.attr, @@ -1047,38 +1032,6 @@ static struct kobj_attribute sys_reipl_nvme_clear_attr = /* CCW reipl device attributes */ DEFINE_IPL_CCW_ATTR_RW(reipl_ccw, device, reipl_block_ccw->ccw); -/* NSS wrapper */ -static ssize_t reipl_nss_loadparm_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) -{ - return reipl_generic_loadparm_show(reipl_block_nss, page); -} - -static ssize_t reipl_nss_loadparm_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) -{ - return reipl_generic_loadparm_store(reipl_block_nss, buf, len); -} - -/* CCW wrapper */ -static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) -{ - return reipl_generic_loadparm_show(reipl_block_ccw, page); -} - -static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) -{ - return reipl_generic_loadparm_store(reipl_block_ccw, buf, len); -} - -static struct kobj_attribute sys_reipl_ccw_loadparm_attr = - __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, - reipl_ccw_loadparm_store); - static ssize_t reipl_ccw_clear_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) { @@ -1251,10 +1204,6 @@ static struct kobj_attribute sys_reipl_nss_name_attr = __ATTR(name, 0644, reipl_nss_name_show, reipl_nss_name_store); -static struct kobj_attribute sys_reipl_nss_loadparm_attr = - __ATTR(loadparm, 0644, reipl_nss_loadparm_show, - reipl_nss_loadparm_store); - static struct attribute *reipl_nss_attrs[] = { &sys_reipl_nss_name_attr.attr, &sys_reipl_nss_loadparm_attr.attr, From 6bb361d5d8eb1dbc9e0b190eeee27a2ac4d1119f Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 29 Jan 2023 16:39:19 +0100 Subject: [PATCH 137/182] s390/ipl: add loadparm parameter to eckd ipl/reipl data commit 87fd22e0ae92 ("s390/ipl: add eckd support") missed to add the loadparm attribute to the new eckd ipl/reipl data. Fixes: 87fd22e0ae92 ("s390/ipl: add eckd support") Cc: Signed-off-by: Sven Schnelle Reviewed-by: Heiko Carstens Signed-off-by: Heiko Carstens --- arch/s390/kernel/ipl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index d7b433261145..5f0f5c86963a 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -593,6 +593,7 @@ static struct attribute *ipl_eckd_attrs[] = { &sys_ipl_type_attr.attr, &sys_ipl_eckd_bootprog_attr.attr, &sys_ipl_eckd_br_chr_attr.attr, + &sys_ipl_ccw_loadparm_attr.attr, &sys_ipl_device_attr.attr, &sys_ipl_secure_attr.attr, &sys_ipl_has_secure_attr.attr, @@ -908,6 +909,7 @@ DEFINE_GENERIC_LOADPARM(fcp); DEFINE_GENERIC_LOADPARM(nvme); DEFINE_GENERIC_LOADPARM(ccw); DEFINE_GENERIC_LOADPARM(nss); +DEFINE_GENERIC_LOADPARM(eckd); static ssize_t reipl_fcp_clear_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) @@ -1129,6 +1131,7 @@ static struct attribute *reipl_eckd_attrs[] = { &sys_reipl_eckd_device_attr.attr, &sys_reipl_eckd_bootprog_attr.attr, &sys_reipl_eckd_br_chr_attr.attr, + &sys_reipl_eckd_loadparm_attr.attr, NULL, }; From 03d4907396f30d1fbe37b67f16eaba9f0b6c3702 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 31 Jan 2023 19:50:25 +0100 Subject: [PATCH 138/182] s390/hmcdrv: use strscpy() instead of strlcpy() Given that strlcpy() is deprecated use strscpy() instead. Signed-off-by: Heiko Carstens --- drivers/s390/char/diag_ftp.c | 4 ++-- drivers/s390/char/sclp_ftp.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c index 36bbd6b6e210..65c7f2d565d8 100644 --- a/drivers/s390/char/diag_ftp.c +++ b/drivers/s390/char/diag_ftp.c @@ -159,8 +159,8 @@ ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) goto out; } - len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); - if (len >= HMCDRV_FTP_FIDENT_MAX) { + len = strscpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); + if (len < 0) { len = -EINVAL; goto out_free; } diff --git a/drivers/s390/char/sclp_ftp.c b/drivers/s390/char/sclp_ftp.c index ec5a0e2b9255..d27e2cbfbccb 100644 --- a/drivers/s390/char/sclp_ftp.c +++ b/drivers/s390/char/sclp_ftp.c @@ -90,7 +90,7 @@ static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) struct completion completion; struct sclp_diag_sccb *sccb; struct sclp_req *req; - size_t len; + ssize_t len; int rc; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -117,9 +117,9 @@ static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) sccb->evbuf.mdd.ftp.length = ftp->len; sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); - len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, + len = strscpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, HMCDRV_FTP_FIDENT_MAX); - if (len >= HMCDRV_FTP_FIDENT_MAX) { + if (len < 0) { rc = -EINVAL; goto out_free; } From 3400c35a4090704e6c465449616ab7e67a9209e7 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 27 Jan 2023 14:03:07 +0100 Subject: [PATCH 139/182] s390/mem_detect: fix detect_memory() error handling Currently if for some reason sclp_early_read_info() fails, sclp_early_get_memsize() will not set max_physmem_end and it will stay uninitialized. Any garbage value other than 0 will lead to detect_memory() taking wrong path or returning a garbage value as max_physmem_end. To avoid that simply initialize max_physmem_end. Fixes: 73045a08cf55 ("s390: unify identity mapping limits handling") Reported-by: Alexander Gordeev Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/mem_detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/boot/mem_detect.c b/arch/s390/boot/mem_detect.c index 7fa1a32ea0f3..0a5821ef4f1f 100644 --- a/arch/s390/boot/mem_detect.c +++ b/arch/s390/boot/mem_detect.c @@ -165,7 +165,7 @@ static void search_mem_end(void) unsigned long detect_memory(void) { - unsigned long max_physmem_end; + unsigned long max_physmem_end = 0; sclp_early_get_memsize(&max_physmem_end); From dfca37d36b746a066bf4d62bce3276c7639d8f09 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 27 Jan 2023 23:27:18 +0100 Subject: [PATCH 140/182] s390/kasan: update kasan memory layout note Kasan shadow memory area has been moved to the end of kernel address space since commit 9a39abb7c9aa ("s390/boot: simplify and fix kernel memory layout setup"). Change kasan memory layout note accordingly. Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/mm/kasan_init.c | 56 ++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index bdfbe0dcb7c6..9d4f3138b0e7 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -271,38 +271,34 @@ void __init kasan_early_init(void) } /* * Current memory layout: - * +- 0 -------------+ +- shadow start -+ - * | 1:1 ram mapping | /| 1/8 ram | - * | | / | | - * +- end of ram ----+ / +----------------+ - * | ... gap ... | / | | - * | |/ | kasan | - * +- shadow start --+ | zero | - * | 1/8 addr space | | page | - * +- shadow end -+ | mapping | - * | ... gap ... |\ | (untracked) | - * +- vmalloc area -+ \ | | - * | vmalloc_size | \ | | - * +- modules vaddr -+ \ +----------------+ - * | 2Gb | \| unmapped | allocated per module - * +-----------------+ +- shadow end ---+ + * +- 0 -------------+ +- shadow start -+ + * |1:1 ident mapping| /|1/8 of ident map| + * | | / | | + * +-end of ident map+ / +----------------+ + * | ... gap ... | / | kasan | + * | | / | zero page | + * +- vmalloc area -+ / | mapping | + * | vmalloc_size | / | (untracked) | + * +- modules vaddr -+ / +----------------+ + * | 2Gb |/ | unmapped | allocated per module + * +- shadow start -+ +----------------+ + * | 1/8 addr space | | zero pg mapping| (untracked) + * +- shadow end ----+---------+- shadow end ---+ * * Current memory layout (KASAN_VMALLOC): - * +- 0 -------------+ +- shadow start -+ - * | 1:1 ram mapping | /| 1/8 ram | - * | | / | | - * +- end of ram ----+ / +----------------+ - * | ... gap ... | / | kasan | - * | |/ | zero | - * +- shadow start --+ | page | - * | 1/8 addr space | | mapping | - * +- shadow end -+ | (untracked) | - * | ... gap ... |\ | | - * +- vmalloc area -+ \ +- vmalloc area -+ - * | vmalloc_size | \ |shallow populate| - * +- modules vaddr -+ \ +- modules area -+ - * | 2Gb | \|shallow populate| - * +-----------------+ +- shadow end ---+ + * +- 0 -------------+ +- shadow start -+ + * |1:1 ident mapping| /|1/8 of ident map| + * | | / | | + * +-end of ident map+ / +----------------+ + * | ... gap ... | / | kasan zero page| (untracked) + * | | / | mapping | + * +- vmalloc area -+ / +----------------+ + * | vmalloc_size | / |shallow populate| + * +- modules vaddr -+ / +----------------+ + * | 2Gb |/ |shallow populate| + * +- shadow start -+ +----------------+ + * | 1/8 addr space | | zero pg mapping| (untracked) + * +- shadow end ----+---------+- shadow end ---+ */ /* populate kasan shadow (for identity mapping and zero page mapping) */ kasan_early_pgtable_populate(__sha(0), __sha(memsize), POPULATE_MAP); From 108303b0a2d27cb14eed565e33e64ad9eefe5d7e Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Sat, 28 Jan 2023 17:35:12 +0100 Subject: [PATCH 141/182] s390/vmem: fix empty page tables cleanup under KASAN Commit b9ff81003cf1 ("s390/vmem: cleanup empty page tables") introduced empty page tables cleanup in vmem code, but when the kernel is built with KASAN enabled the code has no effect due to wrong KASAN shadow memory intersection condition, which effectively ignores any memory range below KASAN shadow. Fix intersection condition to make code work as anticipated. Fixes: b9ff81003cf1 ("s390/vmem: cleanup empty page tables") Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/mm/vmem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 15daf777cf41..10bf5fcbb508 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -298,7 +298,7 @@ static void try_free_pmd_table(pud_t *pud, unsigned long start) if (end > VMALLOC_START) return; #ifdef CONFIG_KASAN - if (start < KASAN_SHADOW_END && KASAN_SHADOW_START > end) + if (start < KASAN_SHADOW_END && end > KASAN_SHADOW_START) return; #endif pmd = pmd_offset(pud, start); @@ -373,7 +373,7 @@ static void try_free_pud_table(p4d_t *p4d, unsigned long start) if (end > VMALLOC_START) return; #ifdef CONFIG_KASAN - if (start < KASAN_SHADOW_END && KASAN_SHADOW_START > end) + if (start < KASAN_SHADOW_END && end > KASAN_SHADOW_START) return; #endif @@ -427,7 +427,7 @@ static void try_free_p4d_table(pgd_t *pgd, unsigned long start) if (end > VMALLOC_START) return; #ifdef CONFIG_KASAN - if (start < KASAN_SHADOW_END && KASAN_SHADOW_START > end) + if (start < KASAN_SHADOW_END && end > KASAN_SHADOW_START) return; #endif From fb9293b9f32d01ee491606a3655054d539b89f66 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Sat, 28 Jan 2023 18:06:40 +0100 Subject: [PATCH 142/182] s390/vmem: remove unnecessary KASAN checks Kasan shadow memory area has been moved to the end of kernel address space since commit 9a39abb7c9aa ("s390/boot: simplify and fix kernel memory layout setup"), therefore skipping any memory ranges above VMALLOC_START in empty page tables cleanup code already handles KASAN shadow memory intersection case and explicit checks could be removed. Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/mm/vmem.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 10bf5fcbb508..4113a7ffa149 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -297,10 +297,7 @@ static void try_free_pmd_table(pud_t *pud, unsigned long start) /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ if (end > VMALLOC_START) return; -#ifdef CONFIG_KASAN - if (start < KASAN_SHADOW_END && end > KASAN_SHADOW_START) - return; -#endif + pmd = pmd_offset(pud, start); for (i = 0; i < PTRS_PER_PMD; i++, pmd++) if (!pmd_none(*pmd)) @@ -372,10 +369,6 @@ static void try_free_pud_table(p4d_t *p4d, unsigned long start) /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ if (end > VMALLOC_START) return; -#ifdef CONFIG_KASAN - if (start < KASAN_SHADOW_END && end > KASAN_SHADOW_START) - return; -#endif pud = pud_offset(p4d, start); for (i = 0; i < PTRS_PER_PUD; i++, pud++) { @@ -426,10 +419,6 @@ static void try_free_p4d_table(pgd_t *pgd, unsigned long start) /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ if (end > VMALLOC_START) return; -#ifdef CONFIG_KASAN - if (start < KASAN_SHADOW_END && end > KASAN_SHADOW_START) - return; -#endif p4d = p4d_offset(pgd, start); for (i = 0; i < PTRS_PER_P4D; i++, p4d++) { From 1e2eb49bb14760cc7ec0c301c705410628bee8e9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 2 Feb 2023 21:36:07 +0100 Subject: [PATCH 143/182] s390/rethook: add local rethook header file Compiling the kernel with CONFIG_KPROBES disabled, but CONFIG_RETHOOK enabled, results in this sparse warning: arch/s390/kernel/rethook.c:26:15: warning: no previous prototype for 'arch_rethook_trampoline_callback' [-Wmissing-prototypes] 26 | unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add a local rethook header file similar to riscv to address this. Reported-by: kernel test robot Fixes: 1a280f48c0e4 ("s390/kprobes: replace kretprobe with rethook") Link: https://lore.kernel.org/all/202302030102.69dZIuJk-lkp@intel.com Signed-off-by: Heiko Carstens --- arch/s390/include/asm/kprobes.h | 1 - arch/s390/kernel/rethook.c | 1 + arch/s390/kernel/rethook.h | 7 +++++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 arch/s390/kernel/rethook.h diff --git a/arch/s390/include/asm/kprobes.h b/arch/s390/include/asm/kprobes.h index 6af75857156b..83f732ca3af4 100644 --- a/arch/s390/include/asm/kprobes.h +++ b/arch/s390/include/asm/kprobes.h @@ -70,7 +70,6 @@ struct kprobe_ctlblk { }; void arch_remove_kprobe(struct kprobe *p); -unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs); int kprobe_fault_handler(struct pt_regs *regs, int trapnr); int kprobe_exceptions_notify(struct notifier_block *self, diff --git a/arch/s390/kernel/rethook.c b/arch/s390/kernel/rethook.c index f2b6237bc35d..af10e6bdd34e 100644 --- a/arch/s390/kernel/rethook.c +++ b/arch/s390/kernel/rethook.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include "rethook.h" void arch_rethook_prepare(struct rethook_node *rh, struct pt_regs *regs, bool mcount) { diff --git a/arch/s390/kernel/rethook.h b/arch/s390/kernel/rethook.h new file mode 100644 index 000000000000..32f069eed3f3 --- /dev/null +++ b/arch/s390/kernel/rethook.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __S390_RETHOOK_H +#define __S390_RETHOOK_H + +unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs); + +#endif From 18e5cb7a5ce30d0fd28c94551509afe43b100118 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 2 Feb 2023 21:47:11 +0100 Subject: [PATCH 144/182] s390/diag: make __diag8c_tmp_amode31 static Get rid of this sparse warning: arch/s390/kernel/diag.c:69:29: warning: symbol '__diag8c_tmp_amode31' was not declared. Should it be static? Fixes: fbaee7464fbb ("s390/tty3270: add support for diag 8c") Signed-off-by: Heiko Carstens --- arch/s390/kernel/diag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/diag.c b/arch/s390/kernel/diag.c index a6a0222a35cf..82079f2d8583 100644 --- a/arch/s390/kernel/diag.c +++ b/arch/s390/kernel/diag.c @@ -66,7 +66,7 @@ static struct diag210 _diag210_tmp_amode31 __section(".amode31.data"); struct diag210 __amode31_ref *__diag210_tmp_amode31 = &_diag210_tmp_amode31; static struct diag8c _diag8c_tmp_amode31 __section(".amode31.data"); -struct diag8c __amode31_ref *__diag8c_tmp_amode31 = &_diag8c_tmp_amode31; +static struct diag8c __amode31_ref *__diag8c_tmp_amode31 = &_diag8c_tmp_amode31; static int show_diag_stat(struct seq_file *m, void *v) { From eb33f9eb304a4c18beb5ba6362eaa5c4beaf40d8 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 27 Jan 2023 14:57:43 +0100 Subject: [PATCH 145/182] s390/mem_detect: rely on diag260() if sclp_early_get_memsize() fails In case sclp_early_get_memsize() fails but diag260() succeeds make sure some sane value is returned. This error scenario is highly unlikely, but this change makes system able to boot in such case. Suggested-by: Alexander Gordeev Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/mem_detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/boot/mem_detect.c b/arch/s390/boot/mem_detect.c index 0a5821ef4f1f..41792a3a5e36 100644 --- a/arch/s390/boot/mem_detect.c +++ b/arch/s390/boot/mem_detect.c @@ -176,7 +176,7 @@ unsigned long detect_memory(void) if (!diag260()) { mem_detect.info_source = MEM_DETECT_DIAG260; - return max_physmem_end; + return max_physmem_end ?: get_mem_detect_end(); } if (max_physmem_end) { From 22476f47b6b7fb7d066c71f67ebc11892adb0849 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Mon, 23 Jan 2023 12:49:47 +0100 Subject: [PATCH 146/182] s390/boot: fix mem_detect extended area allocation Allocation of mem_detect extended area was not considered neither in commit 9641b8cc733f ("s390/ipl: read IPL report at early boot") nor in commit b2d24b97b2a9 ("s390/kernel: add support for kernel address space layout randomization (KASLR)"). As a result mem_detect extended theoretically may overlap with ipl report or randomized kernel image position. But as mem_detect code will allocate extended area only upon exceeding 255 online regions (which should alternate with offline memory regions) it is not seen in practice. To make sure mem_detect extended area does not overlap with ipl report or randomized kernel position extend usage of "safe_addr". Make initrd handling and mem_detect extended area allocation code move it further right and make KASLR takes in into consideration as well. Fixes: 9641b8cc733f ("s390/ipl: read IPL report at early boot") Fixes: b2d24b97b2a9 ("s390/kernel: add support for kernel address space layout randomization (KASLR)") Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 4 +-- arch/s390/boot/kaslr.c | 6 ----- arch/s390/boot/mem_detect.c | 52 ++++++++++++------------------------- arch/s390/boot/startup.c | 21 ++++++++------- 4 files changed, 31 insertions(+), 52 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 830cfabaa6a0..939d27da8fbd 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -33,10 +33,10 @@ struct vmlinux_info { }; void startup_kernel(void); -unsigned long detect_memory(void); +unsigned long detect_memory(unsigned long *safe_addr); bool is_ipl_block_dump(void); void store_ipl_parmblock(void); -unsigned long read_ipl_report(unsigned long safe_offset); +unsigned long read_ipl_report(unsigned long safe_addr); void setup_boot_command_line(void); void parse_boot_command_line(void); void verify_facilities(void); diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c index e8d74d4f62aa..58a8d8c8a100 100644 --- a/arch/s390/boot/kaslr.c +++ b/arch/s390/boot/kaslr.c @@ -174,7 +174,6 @@ unsigned long get_random_base(unsigned long safe_addr) { unsigned long memory_limit = get_mem_detect_end(); unsigned long base_pos, max_pos, kernel_size; - unsigned long kasan_needs; int i; memory_limit = min(memory_limit, ident_map_size); @@ -186,12 +185,7 @@ unsigned long get_random_base(unsigned long safe_addr) */ memory_limit -= kasan_estimate_memory_needs(memory_limit); - if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_data.start && initrd_data.size) { - if (safe_addr < initrd_data.start + initrd_data.size) - safe_addr = initrd_data.start + initrd_data.size; - } safe_addr = ALIGN(safe_addr, THREAD_SIZE); - kernel_size = vmlinux.image_size + vmlinux.bss_size; if (safe_addr + kernel_size > memory_limit) return 0; diff --git a/arch/s390/boot/mem_detect.c b/arch/s390/boot/mem_detect.c index 41792a3a5e36..daa159317183 100644 --- a/arch/s390/boot/mem_detect.c +++ b/arch/s390/boot/mem_detect.c @@ -16,29 +16,10 @@ struct mem_detect_info __bootdata(mem_detect); #define ENTRIES_EXTENDED_MAX \ (256 * (1020 / 2) * sizeof(struct mem_detect_block)) -/* - * To avoid corrupting old kernel memory during dump, find lowest memory - * chunk possible either right after the kernel end (decompressed kernel) or - * after initrd (if it is present and there is no hole between the kernel end - * and initrd) - */ -static void *mem_detect_alloc_extended(void) -{ - unsigned long offset = ALIGN(mem_safe_offset(), sizeof(u64)); - - if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_data.start && initrd_data.size && - initrd_data.start < offset + ENTRIES_EXTENDED_MAX) - offset = ALIGN(initrd_data.start + initrd_data.size, sizeof(u64)); - - return (void *)offset; -} - static struct mem_detect_block *__get_mem_detect_block_ptr(u32 n) { if (n < MEM_INLINED_ENTRIES) return &mem_detect.entries[n]; - if (unlikely(!mem_detect.entries_extended)) - mem_detect.entries_extended = mem_detect_alloc_extended(); return &mem_detect.entries_extended[n - MEM_INLINED_ENTRIES]; } @@ -147,7 +128,7 @@ static int tprot(unsigned long addr) return rc; } -static void search_mem_end(void) +static unsigned long search_mem_end(void) { unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */ unsigned long offset = 0; @@ -159,33 +140,34 @@ static void search_mem_end(void) if (!tprot(pivot << 20)) offset = pivot; } - - add_mem_detect_block(0, (offset + 1) << 20); + return (offset + 1) << 20; } -unsigned long detect_memory(void) +unsigned long detect_memory(unsigned long *safe_addr) { unsigned long max_physmem_end = 0; sclp_early_get_memsize(&max_physmem_end); + mem_detect.entries_extended = (struct mem_detect_block *)ALIGN(*safe_addr, sizeof(u64)); if (!sclp_early_read_storage_info()) { mem_detect.info_source = MEM_DETECT_SCLP_STOR_INFO; - return max_physmem_end; - } - - if (!diag260()) { + } else if (!diag260()) { mem_detect.info_source = MEM_DETECT_DIAG260; - return max_physmem_end ?: get_mem_detect_end(); - } - - if (max_physmem_end) { + max_physmem_end = max_physmem_end ?: get_mem_detect_end(); + } else if (max_physmem_end) { add_mem_detect_block(0, max_physmem_end); mem_detect.info_source = MEM_DETECT_SCLP_READ_INFO; - return max_physmem_end; + } else { + max_physmem_end = search_mem_end(); + add_mem_detect_block(0, max_physmem_end); + mem_detect.info_source = MEM_DETECT_BIN_SEARCH; } - search_mem_end(); - mem_detect.info_source = MEM_DETECT_BIN_SEARCH; - return get_mem_detect_end(); + if (mem_detect.count > MEM_INLINED_ENTRIES) { + *safe_addr += (mem_detect.count - MEM_INLINED_ENTRIES) * + sizeof(struct mem_detect_block); + } + + return max_physmem_end; } diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index c9dfd7e09233..577ebec9971b 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -76,16 +76,17 @@ unsigned long mem_safe_offset(void) } #endif -static void rescue_initrd(unsigned long addr) +static unsigned long rescue_initrd(unsigned long safe_addr) { if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD)) - return; + return safe_addr; if (!initrd_data.start || !initrd_data.size) - return; - if (addr <= initrd_data.start) - return; - memmove((void *)addr, (void *)initrd_data.start, initrd_data.size); - initrd_data.start = addr; + return safe_addr; + if (initrd_data.start < safe_addr) { + memmove((void *)safe_addr, (void *)initrd_data.start, initrd_data.size); + initrd_data.start = safe_addr; + } + return initrd_data.start + initrd_data.size; } static void copy_bootdata(void) @@ -275,6 +276,7 @@ static unsigned long reserve_amode31(unsigned long safe_addr) void startup_kernel(void) { + unsigned long max_physmem_end; unsigned long random_lma; unsigned long safe_addr; unsigned long asce_limit; @@ -294,12 +296,13 @@ void startup_kernel(void) safe_addr = reserve_amode31(safe_addr); safe_addr = read_ipl_report(safe_addr); uv_query_info(); - rescue_initrd(safe_addr); + safe_addr = rescue_initrd(safe_addr); sclp_early_read_info(); setup_boot_command_line(); parse_boot_command_line(); sanitize_prot_virt_host(); - setup_ident_map_size(detect_memory()); + max_physmem_end = detect_memory(&safe_addr); + setup_ident_map_size(max_physmem_end); setup_vmalloc_size(); asce_limit = setup_kernel_memory_layout(); From bf64f0517e5d0d8f3248143fc49535c1d1594b4f Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Sat, 28 Jan 2023 23:55:04 +0100 Subject: [PATCH 147/182] s390/mem_detect: handle online memory limit just once Introduce mem_detect_truncate() to cut any online memory ranges above established identity mapping size, so that mem_detect users wouldn't have to do it over and over again. Suggested-by: Alexander Gordeev Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 3 ++- arch/s390/boot/kaslr.c | 2 -- arch/s390/boot/mem_detect.c | 18 ++++++++++++++++++ arch/s390/boot/startup.c | 3 ++- arch/s390/boot/vmem.c | 15 ++++++--------- arch/s390/kernel/setup.c | 1 - arch/s390/mm/kasan_init.c | 2 -- 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 939d27da8fbd..e91bbb004efb 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -34,6 +34,7 @@ struct vmlinux_info { void startup_kernel(void); unsigned long detect_memory(unsigned long *safe_addr); +void mem_detect_truncate(unsigned long limit); bool is_ipl_block_dump(void); void store_ipl_parmblock(void); unsigned long read_ipl_report(unsigned long safe_addr); @@ -44,7 +45,7 @@ void print_missing_facilities(void); void sclp_early_setup_buffer(void); void print_pgm_check_info(void); unsigned long get_random_base(unsigned long safe_addr); -void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit); +void setup_vmem(unsigned long asce_limit); void __printf(1, 2) decompressor_printk(const char *fmt, ...); void error(char *m); diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c index 58a8d8c8a100..bbf6860ebc45 100644 --- a/arch/s390/boot/kaslr.c +++ b/arch/s390/boot/kaslr.c @@ -176,8 +176,6 @@ unsigned long get_random_base(unsigned long safe_addr) unsigned long base_pos, max_pos, kernel_size; int i; - memory_limit = min(memory_limit, ident_map_size); - /* * Avoid putting kernel in the end of physical memory * which kasan will use for shadow memory and early pgtable diff --git a/arch/s390/boot/mem_detect.c b/arch/s390/boot/mem_detect.c index daa159317183..3058d397a9da 100644 --- a/arch/s390/boot/mem_detect.c +++ b/arch/s390/boot/mem_detect.c @@ -171,3 +171,21 @@ unsigned long detect_memory(unsigned long *safe_addr) return max_physmem_end; } + +void mem_detect_truncate(unsigned long limit) +{ + struct mem_detect_block *block; + int i; + + for (i = 0; i < mem_detect.count; i++) { + block = __get_mem_detect_block_ptr(i); + if (block->start >= limit) { + mem_detect.count = i; + break; + } else if (block->end > limit) { + block->end = (u64)limit; + mem_detect.count = i + 1; + break; + } + } +} diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 577ebec9971b..89beb31e982a 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -305,6 +305,7 @@ void startup_kernel(void) setup_ident_map_size(max_physmem_end); setup_vmalloc_size(); asce_limit = setup_kernel_memory_layout(); + mem_detect_truncate(ident_map_size); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) { random_lma = get_random_base(safe_addr); @@ -336,7 +337,7 @@ void startup_kernel(void) */ clear_bss_section(); handle_relocs(__kaslr_offset); - setup_vmem(ident_map_size, asce_limit); + setup_vmem(asce_limit); copy_bootdata(); if (__kaslr_offset) { diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index a35c251c9123..82ef57827042 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -39,7 +39,7 @@ static void boot_check_oom(void) error("out of memory on boot\n"); } -static void pgtable_populate_init(unsigned long ident_map_size) +static void pgtable_populate_init(void) { unsigned long initrd_end; unsigned long kernel_end; @@ -51,7 +51,7 @@ static void pgtable_populate_init(unsigned long ident_map_size) pgalloc_low = max(pgalloc_low, initrd_end); } - pgalloc_end = round_down(min(ident_map_size, get_mem_detect_end()), PAGE_SIZE); + pgalloc_end = round_down(get_mem_detect_end(), PAGE_SIZE); pgalloc_pos = pgalloc_end; boot_check_oom(); @@ -226,7 +226,7 @@ static void pgtable_populate(unsigned long addr, unsigned long end, enum populat } } -void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit) +void setup_vmem(unsigned long asce_limit) { unsigned long start, end; unsigned long asce_type; @@ -250,13 +250,10 @@ void setup_vmem(unsigned long ident_map_size, unsigned long asce_limit) * To prevent creation of a large page at address 0 first map * the lowcore and create the identity mapping only afterwards. */ - pgtable_populate_init(ident_map_size); + pgtable_populate_init(); pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); - for_each_mem_detect_block(i, &start, &end) { - if (start >= ident_map_size) - break; - pgtable_populate(start, min(end, ident_map_size), POPULATE_ONE2ONE); - } + for_each_mem_detect_block(i, &start, &end) + pgtable_populate(start, end, POPULATE_ONE2ONE); pgtable_populate(__abs_lowcore, __abs_lowcore + sizeof(struct lowcore), POPULATE_ABS_LOWCORE); pgtable_populate(__memcpy_real_area, __memcpy_real_area + PAGE_SIZE, diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 7fad2c02a7c3..d8f41ccfe54e 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -600,7 +600,6 @@ static void __init setup_resources(void) static void __init setup_memory_end(void) { - memblock_remove(ident_map_size, PHYS_ADDR_MAX - ident_map_size); max_pfn = max_low_pfn = PFN_DOWN(ident_map_size); pr_notice("The maximum memory size is %luMB\n", ident_map_size >> 20); } diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 9d4f3138b0e7..1aaea718af73 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -247,8 +247,6 @@ void __init kasan_early_init(void) * The rest [memsize, ident_map_size] if memsize < ident_map_size * could be mapped/unmapped dynamically later during memory hotplug. */ - memsize = min(memsize, ident_map_size); - BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, P4D_SIZE)); BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, P4D_SIZE)); From 3615d01114047ffce3a0942c21e6402c7b49bb3f Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Mon, 30 Jan 2023 00:57:12 +0100 Subject: [PATCH 148/182] s390/mem_detect: add get_mem_detect_online_total() Add a function to get online memory in total. It is supposed to be used in the decompressor as well as during early kernel startup. Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/include/asm/mem_detect.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/s390/include/asm/mem_detect.h b/arch/s390/include/asm/mem_detect.h index 058ac2647eb7..08798ddf5143 100644 --- a/arch/s390/include/asm/mem_detect.h +++ b/arch/s390/include/asm/mem_detect.h @@ -66,6 +66,17 @@ static inline int __get_mem_detect_block(u32 n, unsigned long *start, i < mem_detect.count; \ i++, __get_mem_detect_block(i, p_start, p_end)) +static inline unsigned long get_mem_detect_online_total(void) +{ + unsigned long start, end, total = 0; + int i; + + for_each_mem_detect_block(i, &start, &end) + total += end - start; + + return total; +} + static inline void get_mem_detect_reserved(unsigned long *start, unsigned long *size) { From 8382c963249dafcbe809dea307c2de16d5645579 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 27 Jan 2023 17:08:29 +0100 Subject: [PATCH 149/182] s390/boot: avoid page tables memory in kaslr If kernel is build without KASAN support there is a chance that kernel image is going to be positioned by KASLR code to overlap with identity mapping page tables. When kernel is build with KASAN support enabled memory which is potentially going to be used for page tables and KASAN shadow mapping is accounted for in KASLR with the use of kasan_estimate_memory_needs(). Split this function and introduce vmem_estimate_memory_needs() to cover decompressor's vmem identity mapping page tables. Fixes: bb1520d581a3 ("s390/mm: start kernel with DAT enabled") Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 1 + arch/s390/boot/kaslr.c | 6 ++++-- arch/s390/boot/vmem.c | 7 +++++++ arch/s390/include/asm/kasan.h | 8 ++++---- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index e91bbb004efb..ed85b144119a 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -46,6 +46,7 @@ void sclp_early_setup_buffer(void); void print_pgm_check_info(void); unsigned long get_random_base(unsigned long safe_addr); void setup_vmem(unsigned long asce_limit); +unsigned long vmem_estimate_memory_needs(unsigned long online_mem_total); void __printf(1, 2) decompressor_printk(const char *fmt, ...); void error(char *m); diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c index bbf6860ebc45..9cab7bbbfd18 100644 --- a/arch/s390/boot/kaslr.c +++ b/arch/s390/boot/kaslr.c @@ -172,16 +172,18 @@ static unsigned long position_to_address(unsigned long pos, unsigned long kernel unsigned long get_random_base(unsigned long safe_addr) { + unsigned long online_mem_total = get_mem_detect_online_total(); unsigned long memory_limit = get_mem_detect_end(); unsigned long base_pos, max_pos, kernel_size; int i; /* * Avoid putting kernel in the end of physical memory - * which kasan will use for shadow memory and early pgtable - * mapping allocations. + * which vmem and kasan code will use for shadow memory and + * pgtable mapping allocations. */ memory_limit -= kasan_estimate_memory_needs(memory_limit); + memory_limit -= vmem_estimate_memory_needs(online_mem_total); safe_addr = ALIGN(safe_addr, THREAD_SIZE); kernel_size = vmlinux.image_size + vmlinux.bss_size; diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index 82ef57827042..4e54357ccd00 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -269,3 +269,10 @@ void setup_vmem(unsigned long asce_limit) init_mm.context.asce = S390_lowcore.kernel_asce; } + +unsigned long vmem_estimate_memory_needs(unsigned long online_mem_total) +{ + unsigned long pages = DIV_ROUND_UP(online_mem_total, PAGE_SIZE); + + return DIV_ROUND_UP(pages, _PAGE_ENTRIES) * _PAGE_TABLE_SIZE * 2; +} diff --git a/arch/s390/include/asm/kasan.h b/arch/s390/include/asm/kasan.h index f7244cc16240..e5cfc81d5b61 100644 --- a/arch/s390/include/asm/kasan.h +++ b/arch/s390/include/asm/kasan.h @@ -20,9 +20,9 @@ extern void kasan_early_init(void); * at the very end of available physical memory. To estimate * that, we take into account that kasan would require * 1/8 of available physical memory (for shadow memory) + - * creating page tables for the whole memory + shadow memory - * region (1 + 1/8). To keep page tables estimates simple take - * the double of combined ptes size. + * creating page tables for the shadow memory region. + * To keep page tables estimates simple take the double of + * combined ptes size. * * physmem parameter has to be already adjusted if not entire physical memory * would be used (e.g. due to effect of "mem=" option). @@ -34,7 +34,7 @@ static inline unsigned long kasan_estimate_memory_needs(unsigned long physmem) /* for shadow memory */ kasan_needs = round_up(physmem / 8, PAGE_SIZE); /* for paging structures */ - pages = DIV_ROUND_UP(physmem + kasan_needs, PAGE_SIZE); + pages = DIV_ROUND_UP(kasan_needs, PAGE_SIZE); kasan_needs += DIV_ROUND_UP(pages, _PAGE_ENTRIES) * _PAGE_TABLE_SIZE * 2; return kasan_needs; From 26ced8124a118e8c59d524da9d9f8d5e30502e60 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Mon, 30 Jan 2023 02:11:39 +0100 Subject: [PATCH 150/182] s390/kasan: avoid mapping KASAN shadow for standby memory KASAN common code is able to handle memory hotplug and create KASAN shadow memory on a fly. Online memory ranges are available from mem_detect, use this information to avoid mapping KASAN shadow for standby memory. Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/kaslr.c | 2 +- arch/s390/mm/kasan_init.c | 26 +++++--------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c index 9cab7bbbfd18..70ff68dd1fee 100644 --- a/arch/s390/boot/kaslr.c +++ b/arch/s390/boot/kaslr.c @@ -182,7 +182,7 @@ unsigned long get_random_base(unsigned long safe_addr) * which vmem and kasan code will use for shadow memory and * pgtable mapping allocations. */ - memory_limit -= kasan_estimate_memory_needs(memory_limit); + memory_limit -= kasan_estimate_memory_needs(online_mem_total); memory_limit -= vmem_estimate_memory_needs(online_mem_total); safe_addr = ALIGN(safe_addr, THREAD_SIZE); diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 1aaea718af73..4f6678282726 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -227,26 +227,13 @@ void __init kasan_early_init(void) p4d_t p4d_z = __p4d(__pa(kasan_early_shadow_pud) | _REGION2_ENTRY); unsigned long untracked_end = MODULES_VADDR; unsigned long shadow_alloc_size; - unsigned long memsize; + unsigned long start, end; + int i; kasan_early_detect_facilities(); if (!has_nx) pte_z = clear_pte_bit(pte_z, __pgprot(_PAGE_NOEXEC)); - memsize = get_mem_detect_end(); - if (!memsize) - kasan_early_panic("cannot detect physical memory size\n"); - /* - * Kasan currently supports standby memory but only if it follows - * online memory (default allocation), i.e. no memory holes. - * - memsize represents end of online memory - * - ident_map_size represents online + standby and memory limits - * accounted. - * Kasan maps "memsize" right away. - * [__sha(0), __sha(memsize)] - shadow memory for identity mapping - * The rest [memsize, ident_map_size] if memsize < ident_map_size - * could be mapped/unmapped dynamically later during memory hotplug. - */ BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, P4D_SIZE)); BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, P4D_SIZE)); @@ -256,12 +243,8 @@ void __init kasan_early_init(void) crst_table_init((unsigned long *)kasan_early_shadow_pmd, pmd_val(pmd_z)); memset64((u64 *)kasan_early_shadow_pte, pte_val(pte_z), PTRS_PER_PTE); - shadow_alloc_size = memsize >> KASAN_SHADOW_SCALE_SHIFT; - - if (pgalloc_low + shadow_alloc_size > memsize) - kasan_early_panic("out of memory during initialisation\n"); - if (has_edat) { + shadow_alloc_size = get_mem_detect_online_total() >> KASAN_SHADOW_SCALE_SHIFT; segment_pos = round_down(pgalloc_pos, _SEGMENT_SIZE); segment_low = segment_pos - shadow_alloc_size; segment_low = round_down(segment_low, _SEGMENT_SIZE); @@ -299,7 +282,8 @@ void __init kasan_early_init(void) * +- shadow end ----+---------+- shadow end ---+ */ /* populate kasan shadow (for identity mapping and zero page mapping) */ - kasan_early_pgtable_populate(__sha(0), __sha(memsize), POPULATE_MAP); + for_each_mem_detect_block(i, &start, &end) + kasan_early_pgtable_populate(__sha(start), __sha(end), POPULATE_MAP); if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { untracked_end = VMALLOC_START; /* shallowly populate kasan shadow for vmalloc and modules */ From d1725ca60e8ff7aef3573d29231251c2838ca532 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Thu, 2 Feb 2023 19:21:38 +0100 Subject: [PATCH 151/182] s390/boot: move detect_facilities() after cmd line parsing Facilities setup has to be done after "facilities" command line option parsing, it might set extra or remove existing facilities bits for testing purposes. Fixes: bb1520d581a3 ("s390/mm: start kernel with DAT enabled") Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/startup.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 89beb31e982a..e7e2de386e24 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -283,8 +283,6 @@ void startup_kernel(void) void *img; psw_t psw; - detect_facilities(); - initrd_data.start = parmarea.initrd_start; initrd_data.size = parmarea.initrd_size; oldmem_data.start = parmarea.oldmem_base; @@ -300,6 +298,7 @@ void startup_kernel(void) sclp_early_read_info(); setup_boot_command_line(); parse_boot_command_line(); + detect_facilities(); sanitize_prot_virt_host(); max_physmem_end = detect_memory(&safe_addr); setup_ident_map_size(max_physmem_end); From 6bddf115d0baed3095339024d942d7d1b5e7e4d6 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Thu, 2 Feb 2023 19:22:35 +0100 Subject: [PATCH 152/182] s390/boot: avoid potential amode31 truncation Fixes: bb1520d581a3 ("s390/mm: start kernel with DAT enabled") Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/startup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index e7e2de386e24..f5a7545d3c13 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -271,7 +271,7 @@ static void offset_vmlinux_info(unsigned long offset) static unsigned long reserve_amode31(unsigned long safe_addr) { __amode31_base = PAGE_ALIGN(safe_addr); - return safe_addr + vmlinux.amode31_size; + return __amode31_base + vmlinux.amode31_size; } void startup_kernel(void) From c24def73a2863a8292693ab49a83303213ef3f23 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 3 Feb 2023 08:39:54 +0100 Subject: [PATCH 153/182] watchdog: diag288_wdt: get rid of register asm Using register asm statements has been proven to be very error prone, especially when using code instrumentation where gcc may add function calls, which clobbers register contents in an unexpected way. Therefore, get rid of register asm statements in watchdog code, and make sure this bug class cannot happen. Moreover, remove the register r1 from the clobber list because this register is not changed by DIAG 288. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230203073958.1585738-2-egorenar@linux.ibm.com Signed-off-by: Heiko Carstens --- drivers/watchdog/diag288_wdt.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 6ca5d9515d85..07ebbb709af4 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -73,20 +73,19 @@ MODULE_ALIAS("vmwatchdog"); static int __diag288(unsigned int func, unsigned int timeout, unsigned long action, unsigned int len) { - register unsigned long __func asm("2") = func; - register unsigned long __timeout asm("3") = timeout; - register unsigned long __action asm("4") = action; - register unsigned long __len asm("5") = len; + union register_pair r1 = { .even = func, .odd = timeout, }; + union register_pair r3 = { .even = action, .odd = len, }; int err; err = -EINVAL; asm volatile( - " diag %1, %3, 0x288\n" - "0: la %0, 0\n" + " diag %[r1],%[r3],0x288\n" + "0: la %[err],0\n" "1:\n" EX_TABLE(0b, 1b) - : "+d" (err) : "d"(__func), "d"(__timeout), - "d"(__action), "d"(__len) : "1", "cc", "memory"); + : [err] "+d" (err) + : [r1] "d" (r1.pair), [r3] "d" (r3.pair) + : "cc", "memory"); return err; } From f102dd16ebc82131bbc9eecaead4400062a21858 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 3 Feb 2023 08:39:55 +0100 Subject: [PATCH 154/182] watchdog: diag288_wdt: remove power management Remove power management because s390 no longer supports hibernation since commit 394216275c7d ("s390: remove broken hibernate / power management support"). Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230203073958.1585738-3-egorenar@linux.ibm.com Signed-off-by: Heiko Carstens --- drivers/watchdog/diag288_wdt.c | 65 ++-------------------------------- 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 07ebbb709af4..c8d516ced6d2 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -103,10 +102,6 @@ static int __diag288_lpar(unsigned int func, unsigned int timeout, return __diag288(func, timeout, action, 0); } -static unsigned long wdt_status; - -#define DIAG_WDOG_BUSY 0 - static int wdt_start(struct watchdog_device *dev) { char *ebc_cmd; @@ -114,15 +109,10 @@ static int wdt_start(struct watchdog_device *dev) int ret; unsigned int func; - if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status)) - return -EBUSY; - if (MACHINE_IS_VM) { ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); - if (!ebc_cmd) { - clear_bit(DIAG_WDOG_BUSY, &wdt_status); + if (!ebc_cmd) return -ENOMEM; - } len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); ASCEBC(ebc_cmd, MAX_CMDLEN); EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); @@ -139,7 +129,6 @@ static int wdt_start(struct watchdog_device *dev) if (ret) { pr_err("The watchdog cannot be activated\n"); - clear_bit(DIAG_WDOG_BUSY, &wdt_status); return ret; } return 0; @@ -152,8 +141,6 @@ static int wdt_stop(struct watchdog_device *dev) diag_stat_inc(DIAG_STAT_X288); ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); - clear_bit(DIAG_WDOG_BUSY, &wdt_status); - return ret; } @@ -222,45 +209,6 @@ static struct watchdog_device wdt_dev = { .max_timeout = MAX_INTERVAL, }; -/* - * It makes no sense to go into suspend while the watchdog is running. - * Depending on the memory size, the watchdog might trigger, while we - * are still saving the memory. - */ -static int wdt_suspend(void) -{ - if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status)) { - pr_err("Linux cannot be suspended while the watchdog is in use\n"); - return notifier_from_errno(-EBUSY); - } - return NOTIFY_DONE; -} - -static int wdt_resume(void) -{ - clear_bit(DIAG_WDOG_BUSY, &wdt_status); - return NOTIFY_DONE; -} - -static int wdt_power_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - switch (event) { - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - return wdt_resume(); - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - return wdt_suspend(); - default: - return NOTIFY_DONE; - } -} - -static struct notifier_block wdt_power_notifier = { - .notifier_call = wdt_power_event, -}; - static int __init diag288_init(void) { int ret; @@ -297,21 +245,12 @@ static int __init diag288_init(void) return -EINVAL; } - ret = register_pm_notifier(&wdt_power_notifier); - if (ret) - return ret; - - ret = watchdog_register_device(&wdt_dev); - if (ret) - unregister_pm_notifier(&wdt_power_notifier); - - return ret; + return watchdog_register_device(&wdt_dev); } static void __exit diag288_exit(void) { watchdog_unregister_device(&wdt_dev); - unregister_pm_notifier(&wdt_power_notifier); } module_init(diag288_init); From 221f748ac8633083b7800d4e33a076fce22e341e Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 3 Feb 2023 08:39:56 +0100 Subject: [PATCH 155/182] watchdog: diag288_wdt: unify command buffer handling for diag288 zvm Simplify and de-duplicate code by introducing a common single command buffer allocated once at initialization. Moreover, simplify the interface of __diag288_vm() by accepting ASCII strings as the command parameter and converting it to the EBCDIC format within the function itself. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230203073958.1585738-4-egorenar@linux.ibm.com Signed-off-by: Heiko Carstens --- drivers/watchdog/diag288_wdt.c | 55 +++++++++++++--------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index c8d516ced6d2..c717f47dd4c3 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -69,6 +69,8 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = C MODULE_ALIAS("vmwatchdog"); +static char *cmd_buf; + static int __diag288(unsigned int func, unsigned int timeout, unsigned long action, unsigned int len) { @@ -88,11 +90,18 @@ static int __diag288(unsigned int func, unsigned int timeout, return err; } -static int __diag288_vm(unsigned int func, unsigned int timeout, - char *cmd, size_t len) +static int __diag288_vm(unsigned int func, unsigned int timeout, char *cmd) { + ssize_t len; + + len = strscpy(cmd_buf, cmd, MAX_CMDLEN); + if (len < 0) + return len; + ASCEBC(cmd_buf, MAX_CMDLEN); + EBC_TOUPPER(cmd_buf, MAX_CMDLEN); + diag_stat_inc(DIAG_STAT_X288); - return __diag288(func, timeout, virt_to_phys(cmd), len); + return __diag288(func, timeout, virt_to_phys(cmd_buf), len); } static int __diag288_lpar(unsigned int func, unsigned int timeout, @@ -104,24 +113,14 @@ static int __diag288_lpar(unsigned int func, unsigned int timeout, static int wdt_start(struct watchdog_device *dev) { - char *ebc_cmd; - size_t len; int ret; unsigned int func; if (MACHINE_IS_VM) { - ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); - if (!ebc_cmd) - return -ENOMEM; - len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); - ASCEBC(ebc_cmd, MAX_CMDLEN); - EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); - func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; - ret = __diag288_vm(func, dev->timeout, ebc_cmd, len); + ret = __diag288_vm(func, dev->timeout, wdt_cmd); WARN_ON(ret != 0); - kfree(ebc_cmd); } else { ret = __diag288_lpar(WDT_FUNC_INIT, dev->timeout, LPARWDT_RESTART); @@ -146,19 +145,10 @@ static int wdt_stop(struct watchdog_device *dev) static int wdt_ping(struct watchdog_device *dev) { - char *ebc_cmd; - size_t len; int ret; unsigned int func; if (MACHINE_IS_VM) { - ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); - if (!ebc_cmd) - return -ENOMEM; - len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); - ASCEBC(ebc_cmd, MAX_CMDLEN); - EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); - /* * It seems to be ok to z/VM to use the init function to * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must @@ -167,9 +157,8 @@ static int wdt_ping(struct watchdog_device *dev) func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; - ret = __diag288_vm(func, dev->timeout, ebc_cmd, len); + ret = __diag288_vm(func, dev->timeout, wdt_cmd); WARN_ON(ret != 0); - kfree(ebc_cmd); } else { ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0); } @@ -212,25 +201,20 @@ static struct watchdog_device wdt_dev = { static int __init diag288_init(void) { int ret; - char ebc_begin[] = { - 194, 197, 199, 201, 213 - }; - char *ebc_cmd; watchdog_set_nowayout(&wdt_dev, nowayout_info); if (MACHINE_IS_VM) { - ebc_cmd = kmalloc(sizeof(ebc_begin), GFP_KERNEL); - if (!ebc_cmd) { + cmd_buf = kmalloc(MAX_CMDLEN, GFP_KERNEL); + if (!cmd_buf) { pr_err("The watchdog cannot be initialized\n"); return -ENOMEM; } - memcpy(ebc_cmd, ebc_begin, sizeof(ebc_begin)); - ret = __diag288_vm(WDT_FUNC_INIT, 15, - ebc_cmd, sizeof(ebc_begin)); - kfree(ebc_cmd); + + ret = __diag288_vm(WDT_FUNC_INIT, MIN_INTERVAL, "BEGIN"); if (ret != 0) { pr_err("The watchdog cannot be initialized\n"); + kfree(cmd_buf); return -EINVAL; } } else { @@ -251,6 +235,7 @@ static int __init diag288_init(void) static void __exit diag288_exit(void) { watchdog_unregister_device(&wdt_dev); + kfree(cmd_buf); } module_init(diag288_init); From 379008519819b58a4b5d1adb177f4ce8164a54d0 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 3 Feb 2023 08:39:57 +0100 Subject: [PATCH 156/182] watchdog: diag288_wdt: de-duplicate diag_stat_inc() calls Call diag_stat_inc() from __diag288() to reduce code duplication. Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230203073958.1585738-5-egorenar@linux.ibm.com Signed-off-by: Heiko Carstens --- drivers/watchdog/diag288_wdt.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index c717f47dd4c3..a29ad164b27a 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -78,6 +78,8 @@ static int __diag288(unsigned int func, unsigned int timeout, union register_pair r3 = { .even = action, .odd = len, }; int err; + diag_stat_inc(DIAG_STAT_X288); + err = -EINVAL; asm volatile( " diag %[r1],%[r3],0x288\n" @@ -100,14 +102,12 @@ static int __diag288_vm(unsigned int func, unsigned int timeout, char *cmd) ASCEBC(cmd_buf, MAX_CMDLEN); EBC_TOUPPER(cmd_buf, MAX_CMDLEN); - diag_stat_inc(DIAG_STAT_X288); return __diag288(func, timeout, virt_to_phys(cmd_buf), len); } static int __diag288_lpar(unsigned int func, unsigned int timeout, unsigned long action) { - diag_stat_inc(DIAG_STAT_X288); return __diag288(func, timeout, action, 0); } @@ -135,12 +135,7 @@ static int wdt_start(struct watchdog_device *dev) static int wdt_stop(struct watchdog_device *dev) { - int ret; - - diag_stat_inc(DIAG_STAT_X288); - ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); - - return ret; + return __diag288(WDT_FUNC_CANCEL, 0, 0, 0); } static int wdt_ping(struct watchdog_device *dev) From 20e6ce4818b965463d629e690b23d321722f6e09 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Fri, 3 Feb 2023 08:39:58 +0100 Subject: [PATCH 157/182] watchdog: diag288_wdt: unify lpar and zvm diag288 helpers Change naming of the internal diag288 helper functions to improve overall readability and reduce confusion: * Rename __diag288() to diag288(). * Get rid of the misnamed helper __diag288_lpar() that was used not only on LPARs but also zVM and KVM systems. * Rename __diag288_vm() to diag288_str(). Reviewed-by: Heiko Carstens Signed-off-by: Alexander Egorenkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230203073958.1585738-6-egorenar@linux.ibm.com Signed-off-by: Heiko Carstens --- drivers/watchdog/diag288_wdt.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index a29ad164b27a..4631d0a3866a 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -71,8 +71,8 @@ MODULE_ALIAS("vmwatchdog"); static char *cmd_buf; -static int __diag288(unsigned int func, unsigned int timeout, - unsigned long action, unsigned int len) +static int diag288(unsigned int func, unsigned int timeout, + unsigned long action, unsigned int len) { union register_pair r1 = { .even = func, .odd = timeout, }; union register_pair r3 = { .even = action, .odd = len, }; @@ -92,7 +92,7 @@ static int __diag288(unsigned int func, unsigned int timeout, return err; } -static int __diag288_vm(unsigned int func, unsigned int timeout, char *cmd) +static int diag288_str(unsigned int func, unsigned int timeout, char *cmd) { ssize_t len; @@ -102,13 +102,7 @@ static int __diag288_vm(unsigned int func, unsigned int timeout, char *cmd) ASCEBC(cmd_buf, MAX_CMDLEN); EBC_TOUPPER(cmd_buf, MAX_CMDLEN); - return __diag288(func, timeout, virt_to_phys(cmd_buf), len); -} - -static int __diag288_lpar(unsigned int func, unsigned int timeout, - unsigned long action) -{ - return __diag288(func, timeout, action, 0); + return diag288(func, timeout, virt_to_phys(cmd_buf), len); } static int wdt_start(struct watchdog_device *dev) @@ -119,11 +113,10 @@ static int wdt_start(struct watchdog_device *dev) if (MACHINE_IS_VM) { func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; - ret = __diag288_vm(func, dev->timeout, wdt_cmd); + ret = diag288_str(func, dev->timeout, wdt_cmd); WARN_ON(ret != 0); } else { - ret = __diag288_lpar(WDT_FUNC_INIT, - dev->timeout, LPARWDT_RESTART); + ret = diag288(WDT_FUNC_INIT, dev->timeout, LPARWDT_RESTART, 0); } if (ret) { @@ -135,7 +128,7 @@ static int wdt_start(struct watchdog_device *dev) static int wdt_stop(struct watchdog_device *dev) { - return __diag288(WDT_FUNC_CANCEL, 0, 0, 0); + return diag288(WDT_FUNC_CANCEL, 0, 0, 0); } static int wdt_ping(struct watchdog_device *dev) @@ -152,10 +145,10 @@ static int wdt_ping(struct watchdog_device *dev) func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; - ret = __diag288_vm(func, dev->timeout, wdt_cmd); + ret = diag288_str(func, dev->timeout, wdt_cmd); WARN_ON(ret != 0); } else { - ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0); + ret = diag288(WDT_FUNC_CHANGE, dev->timeout, 0, 0); } if (ret) @@ -206,20 +199,21 @@ static int __init diag288_init(void) return -ENOMEM; } - ret = __diag288_vm(WDT_FUNC_INIT, MIN_INTERVAL, "BEGIN"); + ret = diag288_str(WDT_FUNC_INIT, MIN_INTERVAL, "BEGIN"); if (ret != 0) { pr_err("The watchdog cannot be initialized\n"); kfree(cmd_buf); return -EINVAL; } } else { - if (__diag288_lpar(WDT_FUNC_INIT, 30, LPARWDT_RESTART)) { + if (diag288(WDT_FUNC_INIT, WDT_DEFAULT_TIMEOUT, + LPARWDT_RESTART, 0)) { pr_err("The watchdog cannot be initialized\n"); return -EINVAL; } } - if (__diag288_lpar(WDT_FUNC_CANCEL, 0, 0)) { + if (diag288(WDT_FUNC_CANCEL, 0, 0, 0)) { pr_err("The watchdog cannot be deactivated\n"); return -EINVAL; } From c01016299dc7db5c7b86107b3e129a18dd683574 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 Feb 2023 14:49:39 +0100 Subject: [PATCH 158/182] s390/idle: move idle time accounting to account_idle_time_irq() There is no reason to do idle time accounting in arch_cpu_idle(). Do idle time accounting in account_idle_time_irq(), where it belongs to. The accounted values don't change between account_idle_time_irq() and arch_cpu_idle(); so the result is the same. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/kernel/idle.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index a8aebb5c95cf..0c008b30eb03 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -24,6 +24,7 @@ static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); void account_idle_time_irq(void) { struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); + unsigned long idle_time; u64 cycles_new[8]; int i; @@ -42,12 +43,21 @@ void account_idle_time_irq(void) S390_lowcore.system_timer += S390_lowcore.last_update_timer - idle->timer_idle_enter; S390_lowcore.last_update_timer = idle->timer_idle_exit; + + /* Account time spent with enabled wait psw loaded as idle time. */ + raw_write_seqcount_begin(&idle->seqcount); + idle_time = idle->clock_idle_exit - idle->clock_idle_enter; + idle->clock_idle_enter = 0; + idle->clock_idle_exit = 0; + idle->idle_time += idle_time; + idle->idle_count++; + account_idle_time(cputime_to_nsecs(idle_time)); + raw_write_seqcount_end(&idle->seqcount); } void arch_cpu_idle(void) { struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); - unsigned long idle_time; unsigned long psw_mask; /* Wait for external, I/O or machine check interrupt. */ @@ -57,15 +67,6 @@ void arch_cpu_idle(void) /* psw_idle() returns with interrupts disabled. */ psw_idle(idle, psw_mask); - - /* Account time spent with enabled wait psw loaded as idle time. */ - raw_write_seqcount_begin(&idle->seqcount); - idle_time = idle->clock_idle_exit - idle->clock_idle_enter; - idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; - idle->idle_time += idle_time; - idle->idle_count++; - account_idle_time(cputime_to_nsecs(idle_time)); - raw_write_seqcount_end(&idle->seqcount); raw_local_irq_enable(); } From a9cbc1b471d291c865907542394f1c483b93a811 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 Feb 2023 14:49:40 +0100 Subject: [PATCH 159/182] s390/idle: mark arch_cpu_idle() noinstr linux-next commit ("cpuidle: tracing: Warn about !rcu_is_watching()") adds a new warning which hits on s390's arch_cpu_idle() function: RCU not on for: arch_cpu_idle+0x0/0x28 WARNING: CPU: 2 PID: 0 at include/linux/trace_recursion.h:162 arch_ftrace_ops_list_func+0x24c/0x258 Modules linked in: CPU: 2 PID: 0 Comm: swapper/2 Not tainted 6.2.0-rc6-next-20230202 #4 Hardware name: IBM 8561 T01 703 (z/VM 7.3.0) Krnl PSW : 0404d00180000000 00000000002b55c0 (arch_ftrace_ops_list_func+0x250/0x258) R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:1 PM:0 RI:0 EA:3 Krnl GPRS: c0000000ffffbfff 0000000080000002 0000000000000026 0000000000000000 0000037ffffe3a28 0000037ffffe3a20 0000000000000000 0000000000000000 0000000000000000 0000000000f4acf6 00000000001044f0 0000037ffffe3cb0 0000000000000000 0000000000000000 00000000002b55bc 0000037ffffe3bb8 Krnl Code: 00000000002b55b0: c02000840051 larl %r2,0000000001335652 00000000002b55b6: c0e5fff512d1 brasl %r14,0000000000157b58 #00000000002b55bc: af000000 mc 0,0 >00000000002b55c0: a7f4ffe7 brc 15,00000000002b558e 00000000002b55c4: 0707 bcr 0,%r7 00000000002b55c6: 0707 bcr 0,%r7 00000000002b55c8: eb6ff0480024 stmg %r6,%r15,72(%r15) 00000000002b55ce: b90400ef lgr %r14,%r15 Call Trace: [<00000000002b55c0>] arch_ftrace_ops_list_func+0x250/0x258 ([<00000000002b55bc>] arch_ftrace_ops_list_func+0x24c/0x258) [<0000000000f5f0fc>] ftrace_common+0x1c/0x20 [<00000000001044f6>] arch_cpu_idle+0x6/0x28 [<0000000000f4acf6>] default_idle_call+0x76/0x128 [<00000000001cc374>] do_idle+0xf4/0x1b0 [<00000000001cc6ce>] cpu_startup_entry+0x36/0x40 [<0000000000119d00>] smp_start_secondary+0x140/0x150 [<0000000000f5d2ae>] restart_int_handler+0x6e/0x90 Mark arch_cpu_idle() noinstr like all other architectures with CONFIG_ARCH_WANTS_NO_INSTR (should) have it to fix this. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/kernel/idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 0c008b30eb03..0f4585022e08 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -55,7 +55,7 @@ void account_idle_time_irq(void) raw_write_seqcount_end(&idle->seqcount); } -void arch_cpu_idle(void) +void noinstr arch_cpu_idle(void) { struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); unsigned long psw_mask; From 87f79d886dd8c53bc61b09078764e50e3ac631cc Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 Feb 2023 14:49:41 +0100 Subject: [PATCH 160/182] s390/processor: always inline cpu flag helper functions arch_cpu_idle() is marked noinstr and therefore must only call functions which are also not instrumented. Make sure that cpu flag helper functions are always inlined to avoid that the compiler generates an out-of-line function for e.g. the call within arch_cpu_idle(). Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/processor.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index c907f747d2a0..53172b27a3eb 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -44,17 +44,17 @@ typedef long (*sys_call_ptr_t)(struct pt_regs *regs); -static inline void set_cpu_flag(int flag) +static __always_inline void set_cpu_flag(int flag) { S390_lowcore.cpu_flags |= (1UL << flag); } -static inline void clear_cpu_flag(int flag) +static __always_inline void clear_cpu_flag(int flag) { S390_lowcore.cpu_flags &= ~(1UL << flag); } -static inline int test_cpu_flag(int flag) +static __always_inline int test_cpu_flag(int flag) { return !!(S390_lowcore.cpu_flags & (1UL << flag)); } @@ -63,7 +63,7 @@ static inline int test_cpu_flag(int flag) * Test CIF flag of another CPU. The caller needs to ensure that * CPU hotplug can not happen, e.g. by disabling preemption. */ -static inline int test_cpu_flag_of(int flag, int cpu) +static __always_inline int test_cpu_flag_of(int flag, int cpu) { struct lowcore *lc = lowcore_ptr[cpu]; return !!(lc->cpu_flags & (1UL << flag)); From be20b9d357c722f1fda32b937faf2677ab25cefa Mon Sep 17 00:00:00 2001 From: Vineeth Vijayan Date: Mon, 6 Feb 2023 14:26:31 +0100 Subject: [PATCH 161/182] MAINTAINERS: add entry for s390 SCM driver Storage Class Memory driver support for s390 architecture has been there for a while. The original author of this work, Sebastian Ott has left IBM and I am taking over this module. Adding myself as the upstream maintainer for SCM on s390 architecture. Signed-off-by: Vineeth Vijayan Acked-by: Peter Oberparleiter Signed-off-by: Heiko Carstens --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a36df9ed283d..7a2ce3840b5c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18320,6 +18320,13 @@ F: arch/s390/pci/ F: drivers/pci/hotplug/s390_pci_hpc.c F: Documentation/s390/pci.rst +S390 SCM DRIVER +M: Vineeth Vijayan +L: linux-s390@vger.kernel.org +S: Supported +F: drivers/s390/block/scm* +F: drivers/s390/cio/scm.c + S390 VFIO AP DRIVER M: Tony Krowiak M: Halil Pasic From 1306711ad3bf412852cd357f47a752c761f0095c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 6 Feb 2023 13:56:19 +0100 Subject: [PATCH 162/182] MAINTAINERS: add diag288_wdt driver to s390 maintained files The diag288_wdt watchdog driver is s390 specific. Document who is responsible for this driver. Signed-off-by: Heiko Carstens --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7a2ce3840b5c..44a5e76230e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18260,6 +18260,7 @@ F: Documentation/driver-api/s390-drivers.rst F: Documentation/s390/ F: arch/s390/ F: drivers/s390/ +F: drivers/watchdog/diag288_wdt.c S390 COMMON I/O LAYER M: Vineeth Vijayan From b0b7b43fcc4666232b4ed65d2c8fa08aff1b6042 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 2 Feb 2023 15:47:38 +0100 Subject: [PATCH 163/182] s390/vx: add 64 and 128 bit members to __vector128 struct Add 64 and 128 bit members to __vector128 struct in order to allow reading of the complete value, or the higher or lower part of vector register contents instead of having to use casts. Add an explicit __aligned(4) statement to avoid that the alignment of the structure changes from 4 to 8. This should make sure that no breakage happens because of this change. Reviewed-by: Alexander Gordeev Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/types.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index da034c606314..57139590eb6b 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h @@ -19,8 +19,15 @@ typedef unsigned long addr_t; typedef __signed__ long saddr_t; typedef struct { - __u32 u[4]; -} __vector128; + union { + struct { + __u64 high; + __u64 low; + }; + __uint128_t v; + __u32 u[4]; + }; +} __attribute__((packed, aligned(4))) __vector128; #endif /* __ASSEMBLY__ */ From a02d584e72aa44eb825fb8fddc008bf93f0dfe85 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 2 Feb 2023 15:47:39 +0100 Subject: [PATCH 164/182] s390/vx: use simple assignments to access __vector128 members Use simple assignments to access __vector128 members instead of hard to read casts. Signed-off-by: Heiko Carstens --- arch/s390/include/asm/fpu/internal.h | 4 ++-- arch/s390/kernel/compat_signal.c | 4 ++-- arch/s390/kernel/crash_dump.c | 2 +- arch/s390/kernel/ptrace.c | 6 +++--- arch/s390/kernel/signal.c | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/s390/include/asm/fpu/internal.h b/arch/s390/include/asm/fpu/internal.h index 4a71dbbf76fb..bbdadb1c9efc 100644 --- a/arch/s390/include/asm/fpu/internal.h +++ b/arch/s390/include/asm/fpu/internal.h @@ -27,7 +27,7 @@ static inline void convert_vx_to_fp(freg_t *fprs, __vector128 *vxrs) int i; for (i = 0; i < __NUM_FPRS; i++) - fprs[i] = *(freg_t *)(vxrs + i); + fprs[i].ui = vxrs[i].high; } static inline void convert_fp_to_vx(__vector128 *vxrs, freg_t *fprs) @@ -35,7 +35,7 @@ static inline void convert_fp_to_vx(__vector128 *vxrs, freg_t *fprs) int i; for (i = 0; i < __NUM_FPRS; i++) - *(freg_t *)(vxrs + i) = fprs[i]; + vxrs[i].high = fprs[i].ui; } static inline void fpregs_store(_s390_fp_regs *fpregs, struct fpu *fpu) diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index eee1ad3e1b29..cecedd01d4ec 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -139,7 +139,7 @@ static int save_sigregs_ext32(struct pt_regs *regs, /* Save vector registers to signal stack */ if (MACHINE_HAS_VX) { for (i = 0; i < __NUM_VXRS_LOW; i++) - vxrs[i] = *((__u64 *)(current->thread.fpu.vxrs + i) + 1); + vxrs[i] = current->thread.fpu.vxrs[i].low; if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, sizeof(sregs_ext->vxrs_low)) || __copy_to_user(&sregs_ext->vxrs_high, @@ -173,7 +173,7 @@ static int restore_sigregs_ext32(struct pt_regs *regs, sizeof(sregs_ext->vxrs_high))) return -EFAULT; for (i = 0; i < __NUM_VXRS_LOW; i++) - *((__u64 *)(current->thread.fpu.vxrs + i) + 1) = vxrs[i]; + current->thread.fpu.vxrs[i].low = vxrs[i]; } return 0; } diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index c13b1455ec8c..8a617be28bb4 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -110,7 +110,7 @@ void __init save_area_add_vxrs(struct save_area *sa, __vector128 *vxrs) /* Copy lower halves of vector registers 0-15 */ for (i = 0; i < 16; i++) - memcpy(&sa->vxrs_low[i], &vxrs[i].u[2], 8); + sa->vxrs_low[i] = vxrs[i].low; /* Copy vector registers 16-31 */ memcpy(sa->vxrs_high, vxrs + 16, 16 * sizeof(__vector128)); } diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 53e0209229f8..cf9659e13f03 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -990,7 +990,7 @@ static int s390_vxrs_low_get(struct task_struct *target, if (target == current) save_fpu_regs(); for (i = 0; i < __NUM_VXRS_LOW; i++) - vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1); + vxrs[i] = target->thread.fpu.vxrs[i].low; return membuf_write(&to, vxrs, sizeof(vxrs)); } @@ -1008,12 +1008,12 @@ static int s390_vxrs_low_set(struct task_struct *target, save_fpu_regs(); for (i = 0; i < __NUM_VXRS_LOW; i++) - vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1); + vxrs[i] = target->thread.fpu.vxrs[i].low; rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); if (rc == 0) for (i = 0; i < __NUM_VXRS_LOW; i++) - *((__u64 *)(target->thread.fpu.vxrs + i) + 1) = vxrs[i]; + target->thread.fpu.vxrs[i].low = vxrs[i]; return rc; } diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 38258f817048..d63557d3868c 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -184,7 +184,7 @@ static int save_sigregs_ext(struct pt_regs *regs, /* Save vector registers to signal stack */ if (MACHINE_HAS_VX) { for (i = 0; i < __NUM_VXRS_LOW; i++) - vxrs[i] = *((__u64 *)(current->thread.fpu.vxrs + i) + 1); + vxrs[i] = current->thread.fpu.vxrs[i].low; if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, sizeof(sregs_ext->vxrs_low)) || __copy_to_user(&sregs_ext->vxrs_high, @@ -210,7 +210,7 @@ static int restore_sigregs_ext(struct pt_regs *regs, sizeof(sregs_ext->vxrs_high))) return -EFAULT; for (i = 0; i < __NUM_VXRS_LOW; i++) - *((__u64 *)(current->thread.fpu.vxrs + i) + 1) = vxrs[i]; + current->thread.fpu.vxrs[i].low = vxrs[i]; } return 0; } From be76ea61446095c045641404128bc6862545cda1 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 7 Feb 2023 17:39:42 +0100 Subject: [PATCH 165/182] s390/idle: remove arch_cpu_idle_time() and corresponding code arch_cpu_idle_time() returns the idle time of any given cpu if it is in idle, or zero if not. All if this is racy and partially incorrect. Time stamps taken with store clock extended and store clock fast from different cpus are compared, while the architecture states that this is nothing which can be relied on (see Principles of Operation; Chapter 4, "Setting and Inspecting the Clock"). A more fundamental problem is that the timestamp when a cpu is leaving idle is taken early in the assembler part of the interrupt handler, and this value is only transferred many cycles later to the cpu's per-cpu idle data structure. This per cpu data structure is read by arch_cpu_idle() to tell for which period of time a remote cpu is idle: if only an idle_enter value is present, the assumed idle time of the cpu is calculated by taking a local timestamp and returning the difference of the local timestamp and the idle_enter value. This is potentially incorrect, since the remote cpu may have already left idle, but the taken timestamp may not have been transferred to the per-cpu data structure. This in turn means that too much idle time may be reported for a cpu, and a subsequent calculation of system idle time may result in a smaller value. Instead of coming up with even more complex code trying to fix this, just remove this code, and only account idle time of a cpu, after idle state is left. Another minor bug is that it is assumed that timestamps are non-zero, which is not necessarily the case for timestamps taken with store clock fast. This however is just a very minor problem, since this can only happen when the epoch increases. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/cputime.h | 4 -- arch/s390/include/asm/idle.h | 4 -- arch/s390/kernel/idle.c | 73 ++++----------------------------- 3 files changed, 9 insertions(+), 72 deletions(-) diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h index 7f9284e2a7db..30bb3ec4e5fc 100644 --- a/arch/s390/include/asm/cputime.h +++ b/arch/s390/include/asm/cputime.h @@ -16,10 +16,6 @@ */ #define cputime_to_nsecs(cputime) tod_to_ns(cputime) -u64 arch_cpu_idle_time(int cpu); - -#define arch_idle_time(cpu) arch_cpu_idle_time(cpu) - void account_idle_time_irq(void); #endif /* _S390_CPUTIME_H */ diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h index 5cea629c548e..af72a2b35758 100644 --- a/arch/s390/include/asm/idle.h +++ b/arch/s390/include/asm/idle.h @@ -10,16 +10,12 @@ #include #include -#include struct s390_idle_data { - seqcount_t seqcount; unsigned long idle_count; unsigned long idle_time; unsigned long clock_idle_enter; - unsigned long clock_idle_exit; unsigned long timer_idle_enter; - unsigned long timer_idle_exit; unsigned long mt_cycles_enter[8]; }; diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index 0f4585022e08..dd8351e76539 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -35,24 +35,18 @@ void account_idle_time_irq(void) this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]); } - idle->clock_idle_exit = S390_lowcore.int_clock; - idle->timer_idle_exit = S390_lowcore.sys_enter_timer; + idle_time = S390_lowcore.int_clock - idle->clock_idle_enter; S390_lowcore.steal_timer += idle->clock_idle_enter - S390_lowcore.last_update_clock; - S390_lowcore.last_update_clock = idle->clock_idle_exit; + S390_lowcore.last_update_clock = S390_lowcore.int_clock; S390_lowcore.system_timer += S390_lowcore.last_update_timer - idle->timer_idle_enter; - S390_lowcore.last_update_timer = idle->timer_idle_exit; + S390_lowcore.last_update_timer = S390_lowcore.sys_enter_timer; /* Account time spent with enabled wait psw loaded as idle time. */ - raw_write_seqcount_begin(&idle->seqcount); - idle_time = idle->clock_idle_exit - idle->clock_idle_enter; - idle->clock_idle_enter = 0; - idle->clock_idle_exit = 0; - idle->idle_time += idle_time; - idle->idle_count++; + WRITE_ONCE(idle->idle_time, READ_ONCE(idle->idle_time) + idle_time); + WRITE_ONCE(idle->idle_count, READ_ONCE(idle->idle_count) + 1); account_idle_time(cputime_to_nsecs(idle_time)); - raw_write_seqcount_end(&idle->seqcount); } void noinstr arch_cpu_idle(void) @@ -71,72 +65,23 @@ void noinstr arch_cpu_idle(void) } static ssize_t show_idle_count(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); - unsigned long idle_count; - unsigned int seq; - do { - seq = read_seqcount_begin(&idle->seqcount); - idle_count = READ_ONCE(idle->idle_count); - if (READ_ONCE(idle->clock_idle_enter)) - idle_count++; - } while (read_seqcount_retry(&idle->seqcount, seq)); - return sprintf(buf, "%lu\n", idle_count); + return sysfs_emit(buf, "%lu\n", READ_ONCE(idle->idle_count)); } DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); static ssize_t show_idle_time(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { - unsigned long now, idle_time, idle_enter, idle_exit, in_idle; struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); - unsigned int seq; - do { - seq = read_seqcount_begin(&idle->seqcount); - idle_time = READ_ONCE(idle->idle_time); - idle_enter = READ_ONCE(idle->clock_idle_enter); - idle_exit = READ_ONCE(idle->clock_idle_exit); - } while (read_seqcount_retry(&idle->seqcount, seq)); - in_idle = 0; - now = get_tod_clock(); - if (idle_enter) { - if (idle_exit) { - in_idle = idle_exit - idle_enter; - } else if (now > idle_enter) { - in_idle = now - idle_enter; - } - } - idle_time += in_idle; - return sprintf(buf, "%lu\n", idle_time >> 12); + return sysfs_emit(buf, "%lu\n", READ_ONCE(idle->idle_time) >> 12); } DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); -u64 arch_cpu_idle_time(int cpu) -{ - struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); - unsigned long now, idle_enter, idle_exit, in_idle; - unsigned int seq; - - do { - seq = read_seqcount_begin(&idle->seqcount); - idle_enter = READ_ONCE(idle->clock_idle_enter); - idle_exit = READ_ONCE(idle->clock_idle_exit); - } while (read_seqcount_retry(&idle->seqcount, seq)); - in_idle = 0; - now = get_tod_clock(); - if (idle_enter) { - if (idle_exit) { - in_idle = idle_exit - idle_enter; - } else if (now > idle_enter) { - in_idle = now - idle_enter; - } - } - return cputime_to_nsecs(in_idle); -} - void arch_cpu_idle_enter(void) { } From 2f09c2ea6c649cb010e0782aed3c96108e693cff Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 8 Feb 2023 19:16:45 +0100 Subject: [PATCH 166/182] Revert "s390/mem_detect: do not update output parameters on failure" This reverts commit cbc29f107e51b1cc7d1e7b0bbe0691a1224205f1. Get rid of the following smatch warnings: arch/s390/include/asm/mem_detect.h:86 get_mem_detect_end() error: uninitialized symbol 'end'. arch/s390/include/asm/mem_detect.h:86 get_mem_detect_end() error: uninitialized symbol 'end'. arch/s390/boot/vmem.c:256 setup_vmem() error: uninitialized symbol 'start'. arch/s390/boot/vmem.c:258 setup_vmem() error: uninitialized symbol 'end'. Note that there is no bug in the code. This is purely to silence smatch. Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Heiko Carstens --- arch/s390/include/asm/mem_detect.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/mem_detect.h b/arch/s390/include/asm/mem_detect.h index 08798ddf5143..decd8c4cb799 100644 --- a/arch/s390/include/asm/mem_detect.h +++ b/arch/s390/include/asm/mem_detect.h @@ -40,8 +40,11 @@ void add_mem_detect_block(u64 start, u64 end); static inline int __get_mem_detect_block(u32 n, unsigned long *start, unsigned long *end) { - if (n >= mem_detect.count) + if (n >= mem_detect.count) { + *start = 0; + *end = 0; return -1; + } if (n < MEM_INLINED_ENTRIES) { *start = (unsigned long)mem_detect.entries[n].start; From a64a6d23874c574d30a9816124b2dc37467f3811 Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Thu, 9 Feb 2023 00:00:22 +0100 Subject: [PATCH 167/182] s390: vfio-ap: tighten the NIB validity check The NIB is architecturally invalid if the address designates a storage location that is not installed or if it is zero. Signed-off-by: Halil Pasic Reported-by: Janosch Frank Fixes: ec89b55e3bce ("s390: ap: implement PAPQ AQIC interception in kernel") Reviewed-by: Tony Krowiak Reviewed-by: Pierre Morel Signed-off-by: Heiko Carstens --- drivers/s390/crypto/vfio_ap_ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index cd488639a15b..28a36e016ea9 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -352,6 +352,8 @@ static int vfio_ap_validate_nib(struct kvm_vcpu *vcpu, dma_addr_t *nib) { *nib = vcpu->run->s.regs.gprs[2]; + if (!*nib) + return -EINVAL; if (kvm_is_error_hva(gfn_to_hva(vcpu->kvm, *nib >> PAGE_SHIFT))) return -EINVAL; From 394740d7645ea767795074287769dd26dbd4d782 Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Thu, 9 Feb 2023 00:00:23 +0100 Subject: [PATCH 168/182] s390/ap: fix status returned by ap_aqic() There function ap_aqic() tries to grab the status from the wrong part of the register. Thus we always end up with zeros. Which is wrong, among others, because we detect failures via status.response_code. Signed-off-by: Halil Pasic Reported-by: Janosch Frank Fixes: 159491f3b509 ("s390/ap: rework assembler functions to use unions for in/out register variables") Reviewed-by: Harald Freudenberger Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ap.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index f508f5025e38..876afe46f316 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -239,7 +239,10 @@ static inline struct ap_queue_status ap_aqic(ap_qid_t qid, union { unsigned long value; struct ap_qirq_ctrl qirqctrl; - struct ap_queue_status status; + struct { + u32 _pad; + struct ap_queue_status status; + }; } reg1; unsigned long reg2 = pa_ind; @@ -253,7 +256,7 @@ static inline struct ap_queue_status ap_aqic(ap_qid_t qid, " lgr %[reg1],1\n" /* gr1 (status) into reg1 */ : [reg1] "+&d" (reg1) : [reg0] "d" (reg0), [reg2] "d" (reg2) - : "cc", "0", "1", "2"); + : "cc", "memory", "0", "1", "2"); return reg1.status; } From a2522c80f074c35254974fec39fffe8b8d75befe Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Thu, 9 Feb 2023 00:00:24 +0100 Subject: [PATCH 169/182] s390/ap: fix status returned by ap_qact() Since commit 159491f3b509 ("s390/ap: rework assembler functions to use unions for in/out register variables") the function ap_qact() tries to grab the status from the wrong part of the register. Thus we always end up with zeros. Which is wrong, among others, because we detect failures via status.response_code. Signed-off-by: Halil Pasic Reported-by: Harald Freudenberger Fixes: 159491f3b509 ("s390/ap: rework assembler functions to use unions for in/out register variables") Reviewed-by: Harald Freudenberger Signed-off-by: Heiko Carstens --- arch/s390/include/asm/ap.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index 876afe46f316..57a2d6518d27 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -293,7 +293,10 @@ static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit, unsigned long reg0 = qid | (5UL << 24) | ((ifbit & 0x01) << 22); union { unsigned long value; - struct ap_queue_status status; + struct { + u32 _pad; + struct ap_queue_status status; + }; } reg1; unsigned long reg2; From ac56c666f80df0b1dc9c3ef53d0798b74629a116 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 8 Feb 2023 23:13:51 -0800 Subject: [PATCH 170/182] Documentation: s390: correct spelling Correct spelling problems for Documentation/s390/ as reported by codespell. Signed-off-by: Randy Dunlap Acked-by: Heiko Carstens Link: https://lore.kernel.org/r/20230209071400.31476-16-rdunlap@infradead.org Signed-off-by: Heiko Carstens --- Documentation/s390/pci.rst | 4 ++-- Documentation/s390/vfio-ccw.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/s390/pci.rst b/Documentation/s390/pci.rst index 8157f0cddbc2..a1a72a47dc96 100644 --- a/Documentation/s390/pci.rst +++ b/Documentation/s390/pci.rst @@ -51,7 +51,7 @@ Entries specific to zPCI functions and entries that hold zPCI information. The slot entries are set up using the function identifier (FID) of the PCI function. The format depicted as XXXXXXXX above is 8 hexadecimal digits - with 0 padding and lower case hexadecimal digitis. + with 0 padding and lower case hexadecimal digits. - /sys/bus/pci/slots/XXXXXXXX/power @@ -66,7 +66,7 @@ Entries specific to zPCI functions and entries that hold zPCI information. - function_handle Low-level identifier used for a configured PCI function. - It might be useful for debuging. + It might be useful for debugging. - pchid Model-dependent location of the I/O adapter. diff --git a/Documentation/s390/vfio-ccw.rst b/Documentation/s390/vfio-ccw.rst index a11c24701dcd..37026fa18179 100644 --- a/Documentation/s390/vfio-ccw.rst +++ b/Documentation/s390/vfio-ccw.rst @@ -176,7 +176,7 @@ The process of how these work together. Use the 'mdev_create' sysfs file, we need to manually create one (and only one for our case) mediated device. 3. vfio_mdev.ko drives the mediated ccw device. - vfio_mdev is also the vfio device drvier. It will probe the mdev and + vfio_mdev is also the vfio device driver. It will probe the mdev and add it to an iommu_group and a vfio_group. Then we could pass through the mdev to a guest. From d939474b3d92624744a334c9e5f58ce3934584b5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Sun, 5 Feb 2023 18:17:04 -0500 Subject: [PATCH 171/182] s390/mm: define private VM_FAULT_* reasons from top bits The current definition already collapse with the generic definition of vm_fault_reason. Move the private definitions to allocate bits from the top of uint so they won't collapse anymore. Signed-off-by: Peter Xu Link: https://lore.kernel.org/r/20230205231704.909536-4-peterx@redhat.com Signed-off-by: Heiko Carstens --- arch/s390/mm/fault.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 9649d9382e0a..65930c1928f8 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -46,11 +46,15 @@ #define __SUBCODE_MASK 0x0600 #define __PF_RES_FIELD 0x8000000000000000ULL -#define VM_FAULT_BADCONTEXT ((__force vm_fault_t) 0x010000) -#define VM_FAULT_BADMAP ((__force vm_fault_t) 0x020000) -#define VM_FAULT_BADACCESS ((__force vm_fault_t) 0x040000) -#define VM_FAULT_SIGNAL ((__force vm_fault_t) 0x080000) -#define VM_FAULT_PFAULT ((__force vm_fault_t) 0x100000) +/* + * Allocate private vm_fault_reason from top. Please make sure it won't + * collide with vm_fault_reason. + */ +#define VM_FAULT_BADCONTEXT ((__force vm_fault_t)0x80000000) +#define VM_FAULT_BADMAP ((__force vm_fault_t)0x40000000) +#define VM_FAULT_BADACCESS ((__force vm_fault_t)0x20000000) +#define VM_FAULT_SIGNAL ((__force vm_fault_t)0x10000000) +#define VM_FAULT_PFAULT ((__force vm_fault_t)0x8000000) enum fault_type { KERNEL_FAULT, From 0807b856521f3313d3912ebb52a9144215c4ff08 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Mon, 6 Feb 2023 17:48:21 +0100 Subject: [PATCH 172/182] s390/mm: add support for RDP (Reset DAT-Protection) RDP instruction allows to reset DAT-protection bit in a PTE, with less CPU synchronization overhead than IPTE instruction. In particular, IPTE can cause machine-wide synchronization overhead, and excessive IPTE usage can negatively impact machine performance. RDP can be used instead of IPTE, if the new PTE only differs in SW bits and _PAGE_PROTECT HW bit, for PTE protection changes from RO to RW. SW PTE bit changes are allowed, e.g. for dirty and young tracking, but none of the other HW-defined part of the PTE must change. This is because the architecture forbids such changes to an active and valid PTE, which is why invalidation with IPTE is always used first, before writing a new entry. The RDP optimization helps mainly for fault-driven SW dirty-bit tracking. Writable PTEs are initially always mapped with HW _PAGE_PROTECT bit set, to allow SW dirty-bit accounting on first write protection fault, where the DAT-protection would then be reset. The reset is now done with RDP instead of IPTE, if RDP instruction is available. RDP cannot always guarantee that the DAT-protection reset is propagated to all CPUs immediately. This means that spurious TLB protection faults on other CPUs can now occur. For this, common code provides a flush_tlb_fix_spurious_fault() handler, which will now be used to do a CPU-local TLB flush. However, this will clear the whole TLB of a CPU, and not just the affected entry. For more fine-grained flushing, by simply doing a (local) RDP again, flush_tlb_fix_spurious_fault() would need to also provide the PTE pointer. Note that spurious TLB protection faults cannot really be distinguished from racing pagetable updates, where another thread already installed the correct PTE. In such a case, the local TLB flush would be unnecessary overhead, but overall reduction of CPU synchronization overhead by not using IPTE is still expected to be beneficial. Reviewed-by: Alexander Gordeev Signed-off-by: Gerald Schaefer Signed-off-by: Heiko Carstens --- arch/s390/include/asm/pgtable.h | 62 ++++++++++++++++++++++++++++++++- arch/s390/include/asm/setup.h | 2 ++ arch/s390/kernel/early.c | 2 ++ arch/s390/mm/pgtable.c | 25 +++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 0f1eba005f6d..b87ca864d27d 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -182,12 +182,20 @@ static inline int is_module_addr(void *addr) #define _PAGE_SOFT_DIRTY 0x000 #endif +#define _PAGE_SW_BITS 0xffUL /* All SW bits */ + #define _PAGE_SWP_EXCLUSIVE _PAGE_LARGE /* SW pte exclusive swap bit */ /* Set of bits not changed in pte_modify */ #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_DIRTY | \ _PAGE_YOUNG | _PAGE_SOFT_DIRTY) +/* + * Mask of bits that must not be changed with RDP. Allow only _PAGE_PROTECT + * HW bit and all SW bits. + */ +#define _PAGE_RDP_MASK ~(_PAGE_PROTECT | _PAGE_SW_BITS) + /* * handle_pte_fault uses pte_present and pte_none to find out the pte type * WITHOUT holding the page table lock. The _PAGE_PRESENT bit is used to @@ -1052,6 +1060,19 @@ static inline pte_t pte_mkhuge(pte_t pte) #define IPTE_NODAT 0x400 #define IPTE_GUEST_ASCE 0x800 +static __always_inline void __ptep_rdp(unsigned long addr, pte_t *ptep, + unsigned long opt, unsigned long asce, + int local) +{ + unsigned long pto; + + pto = __pa(ptep) & ~(PTRS_PER_PTE * sizeof(pte_t) - 1); + asm volatile(".insn rrf,0xb98b0000,%[r1],%[r2],%[asce],%[m4]" + : "+m" (*ptep) + : [r1] "a" (pto), [r2] "a" ((addr & PAGE_MASK) | opt), + [asce] "a" (asce), [m4] "i" (local)); +} + static __always_inline void __ptep_ipte(unsigned long address, pte_t *ptep, unsigned long opt, unsigned long asce, int local) @@ -1202,6 +1223,42 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, ptep_xchg_lazy(mm, addr, ptep, pte_wrprotect(pte)); } +/* + * Check if PTEs only differ in _PAGE_PROTECT HW bit, but also allow SW PTE + * bits in the comparison. Those might change e.g. because of dirty and young + * tracking. + */ +static inline int pte_allow_rdp(pte_t old, pte_t new) +{ + /* + * Only allow changes from RO to RW + */ + if (!(pte_val(old) & _PAGE_PROTECT) || pte_val(new) & _PAGE_PROTECT) + return 0; + + return (pte_val(old) & _PAGE_RDP_MASK) == (pte_val(new) & _PAGE_RDP_MASK); +} + +static inline void flush_tlb_fix_spurious_fault(struct vm_area_struct *vma, + unsigned long address) +{ + /* + * RDP might not have propagated the PTE protection reset to all CPUs, + * so there could be spurious TLB protection faults. + * NOTE: This will also be called when a racing pagetable update on + * another thread already installed the correct PTE. Both cases cannot + * really be distinguished. + * Therefore, only do the local TLB flush when RDP can be used, to avoid + * unnecessary overhead. + */ + if (MACHINE_HAS_RDP) + asm volatile("ptlb" : : : "memory"); +} +#define flush_tlb_fix_spurious_fault flush_tlb_fix_spurious_fault + +void ptep_reset_dat_prot(struct mm_struct *mm, unsigned long addr, pte_t *ptep, + pte_t new); + #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS static inline int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, @@ -1209,7 +1266,10 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma, { if (pte_same(*ptep, entry)) return 0; - ptep_xchg_direct(vma->vm_mm, addr, ptep, entry); + if (MACHINE_HAS_RDP && !mm_has_pgste(vma->vm_mm) && pte_allow_rdp(*ptep, entry)) + ptep_reset_dat_prot(vma->vm_mm, addr, ptep, entry); + else + ptep_xchg_direct(vma->vm_mm, addr, ptep, entry); return 1; } diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 177bf6deaa27..3a1f8825bc7d 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -34,6 +34,7 @@ #define MACHINE_FLAG_GS BIT(16) #define MACHINE_FLAG_SCC BIT(17) #define MACHINE_FLAG_PCI_MIO BIT(18) +#define MACHINE_FLAG_RDP BIT(19) #define LPP_MAGIC BIT(31) #define LPP_PID_MASK _AC(0xffffffff, UL) @@ -99,6 +100,7 @@ extern unsigned long mio_wb_bit_mask; #define MACHINE_HAS_GS (S390_lowcore.machine_flags & MACHINE_FLAG_GS) #define MACHINE_HAS_SCC (S390_lowcore.machine_flags & MACHINE_FLAG_SCC) #define MACHINE_HAS_PCI_MIO (S390_lowcore.machine_flags & MACHINE_FLAG_PCI_MIO) +#define MACHINE_HAS_RDP (S390_lowcore.machine_flags & MACHINE_FLAG_RDP) /* * Console mode. Override with conmode= diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 9cfd9f4fc927..59eba19ae0f2 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -226,6 +226,8 @@ static __init void detect_machine_facilities(void) S390_lowcore.machine_flags |= MACHINE_FLAG_PCI_MIO; /* the control bit is set during PCI initialization */ } + if (test_facility(194)) + S390_lowcore.machine_flags |= MACHINE_FLAG_RDP; } static inline void save_vector_registers(void) diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 4909dcd762e8..6effb24de6d9 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -302,6 +302,31 @@ pte_t ptep_xchg_direct(struct mm_struct *mm, unsigned long addr, } EXPORT_SYMBOL(ptep_xchg_direct); +/* + * Caller must check that new PTE only differs in _PAGE_PROTECT HW bit, so that + * RDP can be used instead of IPTE. See also comments at pte_allow_rdp(). + */ +void ptep_reset_dat_prot(struct mm_struct *mm, unsigned long addr, pte_t *ptep, + pte_t new) +{ + preempt_disable(); + atomic_inc(&mm->context.flush_count); + if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) + __ptep_rdp(addr, ptep, 0, 0, 1); + else + __ptep_rdp(addr, ptep, 0, 0, 0); + /* + * PTE is not invalidated by RDP, only _PAGE_PROTECT is cleared. That + * means it is still valid and active, and must not be changed according + * to the architecture. But writing a new value that only differs in SW + * bits is allowed. + */ + set_pte(ptep, new); + atomic_dec(&mm->context.flush_count); + preempt_enable(); +} +EXPORT_SYMBOL(ptep_reset_dat_prot); + pte_t ptep_xchg_lazy(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t new) { From 55d169c87db1d0faa95313809f18f8b49cafdd75 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 10 Feb 2023 14:09:38 +0100 Subject: [PATCH 173/182] s390/vx: remove __uint128_t type from __vector128 struct again The __uint128_t member was only added for future convenience to the __vector128 struct. However this is a uapi header file, 31/32 bit (aka compat layer) is still supported, but doesn't know anything about this type: /usr/include/asm/types.h:27:17: error: unknown type name __uint128_t 27 | __uint128_t v; Therefore remove it again. Fixes: b0b7b43fcc46 ("s390/vx: add 64 and 128 bit members to __vector128 struct") Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index 57139590eb6b..805fccbe0040 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h @@ -24,7 +24,6 @@ typedef struct { __u64 high; __u64 low; }; - __uint128_t v; __u32 u[4]; }; } __attribute__((packed, aligned(4))) __vector128; From af0735269b72333d06e9677cb843bf5ce689a38c Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 10 Feb 2023 15:47:06 +0100 Subject: [PATCH 174/182] s390/mem_detect: do not truncate online memory ranges info Commit bf64f0517e5d ("s390/mem_detect: handle online memory limit just once") introduced truncation of mem_detect online ranges based on identity mapping size. For kdump case however the full set of online memory ranges has to be feed into memblock_physmem_add so that crashed system memory could be extracted. Instead of truncating introduce a "usable limit" which is respected by mem_detect api. Also add extra online memory ranges iterator which still provides full set of online memory ranges disregarding the "usable limit". Fixes: bf64f0517e5d ("s390/mem_detect: handle online memory limit just once") Reported-by: Alexander Egorenkov Tested-by: Alexander Egorenkov Reviewed-by: Alexander Gordeev Signed-off-by: Vasily Gorbik Signed-off-by: Heiko Carstens --- arch/s390/boot/boot.h | 2 +- arch/s390/boot/kaslr.c | 10 +++++----- arch/s390/boot/mem_detect.c | 12 +++++------ arch/s390/boot/startup.c | 2 +- arch/s390/boot/vmem.c | 2 +- arch/s390/include/asm/mem_detect.h | 32 ++++++++++++++++++++---------- arch/s390/kernel/setup.c | 4 ++-- arch/s390/mm/kasan_init.c | 4 ++-- 8 files changed, 40 insertions(+), 28 deletions(-) diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index ed85b144119a..58ce701d6110 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -34,7 +34,7 @@ struct vmlinux_info { void startup_kernel(void); unsigned long detect_memory(unsigned long *safe_addr); -void mem_detect_truncate(unsigned long limit); +void mem_detect_set_usable_limit(unsigned long limit); bool is_ipl_block_dump(void); void store_ipl_parmblock(void); unsigned long read_ipl_report(unsigned long safe_addr); diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c index 70ff68dd1fee..3e3d846400b4 100644 --- a/arch/s390/boot/kaslr.c +++ b/arch/s390/boot/kaslr.c @@ -132,7 +132,7 @@ static unsigned long count_valid_kernel_positions(unsigned long kernel_size, unsigned long start, end, pos = 0; int i; - for_each_mem_detect_block(i, &start, &end) { + for_each_mem_detect_usable_block(i, &start, &end) { if (_min >= end) continue; if (start >= _max) @@ -153,7 +153,7 @@ static unsigned long position_to_address(unsigned long pos, unsigned long kernel unsigned long start, end; int i; - for_each_mem_detect_block(i, &start, &end) { + for_each_mem_detect_usable_block(i, &start, &end) { if (_min >= end) continue; if (start >= _max) @@ -172,7 +172,7 @@ static unsigned long position_to_address(unsigned long pos, unsigned long kernel unsigned long get_random_base(unsigned long safe_addr) { - unsigned long online_mem_total = get_mem_detect_online_total(); + unsigned long usable_total = get_mem_detect_usable_total(); unsigned long memory_limit = get_mem_detect_end(); unsigned long base_pos, max_pos, kernel_size; int i; @@ -182,8 +182,8 @@ unsigned long get_random_base(unsigned long safe_addr) * which vmem and kasan code will use for shadow memory and * pgtable mapping allocations. */ - memory_limit -= kasan_estimate_memory_needs(online_mem_total); - memory_limit -= vmem_estimate_memory_needs(online_mem_total); + memory_limit -= kasan_estimate_memory_needs(usable_total); + memory_limit -= vmem_estimate_memory_needs(usable_total); safe_addr = ALIGN(safe_addr, THREAD_SIZE); kernel_size = vmlinux.image_size + vmlinux.bss_size; diff --git a/arch/s390/boot/mem_detect.c b/arch/s390/boot/mem_detect.c index 3058d397a9da..35f4ba11f7fd 100644 --- a/arch/s390/boot/mem_detect.c +++ b/arch/s390/boot/mem_detect.c @@ -172,20 +172,20 @@ unsigned long detect_memory(unsigned long *safe_addr) return max_physmem_end; } -void mem_detect_truncate(unsigned long limit) +void mem_detect_set_usable_limit(unsigned long limit) { struct mem_detect_block *block; int i; + /* make sure mem_detect.usable ends up within online memory block */ for (i = 0; i < mem_detect.count; i++) { block = __get_mem_detect_block_ptr(i); - if (block->start >= limit) { - mem_detect.count = i; + if (block->start >= limit) break; - } else if (block->end > limit) { - block->end = (u64)limit; - mem_detect.count = i + 1; + if (block->end >= limit) { + mem_detect.usable = limit; break; } + mem_detect.usable = block->end; } } diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index f5a7545d3c13..11413f0baabc 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -304,7 +304,7 @@ void startup_kernel(void) setup_ident_map_size(max_physmem_end); setup_vmalloc_size(); asce_limit = setup_kernel_memory_layout(); - mem_detect_truncate(ident_map_size); + mem_detect_set_usable_limit(ident_map_size); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) { random_lma = get_random_base(safe_addr); diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index 4e54357ccd00..4d1d0d8e99cb 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -252,7 +252,7 @@ void setup_vmem(unsigned long asce_limit) */ pgtable_populate_init(); pgtable_populate(0, sizeof(struct lowcore), POPULATE_ONE2ONE); - for_each_mem_detect_block(i, &start, &end) + for_each_mem_detect_usable_block(i, &start, &end) pgtable_populate(start, end, POPULATE_ONE2ONE); pgtable_populate(__abs_lowcore, __abs_lowcore + sizeof(struct lowcore), POPULATE_ABS_LOWCORE); diff --git a/arch/s390/include/asm/mem_detect.h b/arch/s390/include/asm/mem_detect.h index decd8c4cb799..f9e7354036d2 100644 --- a/arch/s390/include/asm/mem_detect.h +++ b/arch/s390/include/asm/mem_detect.h @@ -30,6 +30,7 @@ struct mem_detect_block { struct mem_detect_info { u32 count; u8 info_source; + unsigned long usable; struct mem_detect_block entries[MEM_INLINED_ENTRIES]; struct mem_detect_block *entries_extended; }; @@ -38,7 +39,7 @@ extern struct mem_detect_info mem_detect; void add_mem_detect_block(u64 start, u64 end); static inline int __get_mem_detect_block(u32 n, unsigned long *start, - unsigned long *end) + unsigned long *end, bool respect_usable_limit) { if (n >= mem_detect.count) { *start = 0; @@ -53,28 +54,37 @@ static inline int __get_mem_detect_block(u32 n, unsigned long *start, *start = (unsigned long)mem_detect.entries_extended[n - MEM_INLINED_ENTRIES].start; *end = (unsigned long)mem_detect.entries_extended[n - MEM_INLINED_ENTRIES].end; } + + if (respect_usable_limit && mem_detect.usable) { + if (*start >= mem_detect.usable) + return -1; + if (*end > mem_detect.usable) + *end = mem_detect.usable; + } return 0; } /** - * for_each_mem_detect_block - early online memory range iterator + * for_each_mem_detect_usable_block - early online memory range iterator * @i: an integer used as loop variable * @p_start: ptr to unsigned long for start address of the range * @p_end: ptr to unsigned long for end address of the range * - * Walks over detected online memory ranges. + * Walks over detected online memory ranges below usable limit. */ -#define for_each_mem_detect_block(i, p_start, p_end) \ - for (i = 0, __get_mem_detect_block(i, p_start, p_end); \ - i < mem_detect.count; \ - i++, __get_mem_detect_block(i, p_start, p_end)) +#define for_each_mem_detect_usable_block(i, p_start, p_end) \ + for (i = 0; !__get_mem_detect_block(i, p_start, p_end, true); i++) -static inline unsigned long get_mem_detect_online_total(void) +/* Walks over all detected online memory ranges disregarding usable limit. */ +#define for_each_mem_detect_block(i, p_start, p_end) \ + for (i = 0; !__get_mem_detect_block(i, p_start, p_end, false); i++) + +static inline unsigned long get_mem_detect_usable_total(void) { unsigned long start, end, total = 0; int i; - for_each_mem_detect_block(i, &start, &end) + for_each_mem_detect_usable_block(i, &start, &end) total += end - start; return total; @@ -95,8 +105,10 @@ static inline unsigned long get_mem_detect_end(void) unsigned long start; unsigned long end; + if (mem_detect.usable) + return mem_detect.usable; if (mem_detect.count) { - __get_mem_detect_block(mem_detect.count - 1, &start, &end); + __get_mem_detect_block(mem_detect.count - 1, &start, &end, false); return end; } return 0; diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index d8f41ccfe54e..8ec5cdf9dadc 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -772,10 +772,10 @@ static void __init memblock_add_mem_detect_info(void) get_mem_info_source(), mem_detect.info_source); /* keep memblock lists close to the kernel */ memblock_set_bottom_up(true); - for_each_mem_detect_block(i, &start, &end) { + for_each_mem_detect_usable_block(i, &start, &end) memblock_add(start, end - start); + for_each_mem_detect_block(i, &start, &end) memblock_physmem_add(start, end - start); - } memblock_set_bottom_up(false); memblock_set_node(0, ULONG_MAX, &memblock.memory, 0); } diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 4f6678282726..ef89a5f26853 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -244,7 +244,7 @@ void __init kasan_early_init(void) memset64((u64 *)kasan_early_shadow_pte, pte_val(pte_z), PTRS_PER_PTE); if (has_edat) { - shadow_alloc_size = get_mem_detect_online_total() >> KASAN_SHADOW_SCALE_SHIFT; + shadow_alloc_size = get_mem_detect_usable_total() >> KASAN_SHADOW_SCALE_SHIFT; segment_pos = round_down(pgalloc_pos, _SEGMENT_SIZE); segment_low = segment_pos - shadow_alloc_size; segment_low = round_down(segment_low, _SEGMENT_SIZE); @@ -282,7 +282,7 @@ void __init kasan_early_init(void) * +- shadow end ----+---------+- shadow end ---+ */ /* populate kasan shadow (for identity mapping and zero page mapping) */ - for_each_mem_detect_block(i, &start, &end) + for_each_mem_detect_usable_block(i, &start, &end) kasan_early_pgtable_populate(__sha(start), __sha(end), POPULATE_MAP); if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { untracked_end = VMALLOC_START; From adf1e17edc65560ea5615d35ded65834cbf33422 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 12 Feb 2023 18:00:56 +0100 Subject: [PATCH 175/182] s390/entry: remove toolchain dependent micro-optimization Get rid of CONFIG_AS_IS_LLVM in entry.S to make the code a bit more readable. This removes a micro-optimization, but given that the llvm IAS limitation will likely stay, just use the version that works with llvm. See commit 4c25f0ff6336 ("s390/entry: workaround llvm's IAS limitations") for further details. Signed-off-by: Heiko Carstens --- arch/s390/kernel/entry.S | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 0f423e9df095..c8d8c9960936 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -137,19 +137,13 @@ _LPP_OFFSET = __LC_LPP lgr %r14,\reg larl %r13,\start slgr %r14,%r13 -#ifdef CONFIG_AS_IS_LLVM clgfrl %r14,.Lrange_size\@ -#else - clgfi %r14,\end - \start -#endif jhe \outside_label -#ifdef CONFIG_AS_IS_LLVM .section .rodata, "a" .align 4 .Lrange_size\@: .long \end - \start .previous -#endif .endm .macro SIEEXIT From 1c06bb87afb2d95b8e9f4f2e3d0d6772c68f3e76 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Fri, 10 Feb 2023 18:42:27 +0100 Subject: [PATCH 176/182] vfio/ccw: remove WARN_ON during shutdown The logic in vfio_ccw_sch_shutdown() always assumed that the input subchannel would point to a vfio_ccw_private struct, without checking that one exists. The blamed commit put in a check for this scenario, to prevent the possibility of a missing private. The trouble is that check was put alongside a WARN_ON(), presuming that such a scenario would be a cause for concern. But this can be triggered by binding a subchannel to vfio-ccw, and rebooting the system before starting the mdev (via "mdevctl start" or similar) or after stopping it. In those cases, shutdown doesn't need to worry because either the private was never allocated, or it was cleaned up by vfio_ccw_mdev_remove(). Remove the WARN_ON() piece of this check, since there are plausible scenarios where private would be NULL in this path. Fixes: 9e6f07cd1eaa ("vfio/ccw: create a parent struct") Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Link: https://lore.kernel.org/r/20230210174227.2256424-1-farman@linux.ibm.com Signed-off-by: Heiko Carstens --- drivers/s390/cio/vfio_ccw_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index 54aba7cceb33..ff538a086fc7 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -225,7 +225,7 @@ static void vfio_ccw_sch_shutdown(struct subchannel *sch) struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev); struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev); - if (WARN_ON(!private)) + if (!private) return; vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE); From 8eff2e2410cfe941207cb17ab322b0e6ce780f54 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sun, 12 Feb 2023 17:37:48 +0100 Subject: [PATCH 177/182] s390: remove confusing comment from uapi types header file The comment for addr_t doesn't make too much sense. Given that also the formatting is incorrect, just remove it. Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/types.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index 805fccbe0040..84457dbb26b4 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h @@ -12,10 +12,7 @@ #ifndef __ASSEMBLY__ -/* A address type so that arithmetic can be done on it & it can be upgraded to - 64 bit when necessary -*/ -typedef unsigned long addr_t; +typedef unsigned long addr_t; typedef __signed__ long saddr_t; typedef struct { From ad0faae6ceab7d138dddf9a58eab7151c519e0d3 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Tue, 8 Feb 2022 16:59:47 +0100 Subject: [PATCH 178/182] s390/zcrypt: introduce ctfm field in struct CPRBX Modify the CPRBX struct to expose a new field ctfm for use with hardware command filtering within a CEX8 crypto card in CCA coprocessor mode. The field replaces a reserved byte padding field so that the layout of the struct and the size does not change. The new field is used only by user space applications which may use this to expose the HW filtering facilities in the crypto firmware layers. Signed-off-by: Harald Freudenberger Signed-off-by: Heiko Carstens --- arch/s390/include/uapi/asm/zcrypt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/uapi/asm/zcrypt.h b/arch/s390/include/uapi/asm/zcrypt.h index d83713f67530..f4785abe1b9f 100644 --- a/arch/s390/include/uapi/asm/zcrypt.h +++ b/arch/s390/include/uapi/asm/zcrypt.h @@ -85,7 +85,8 @@ struct ica_rsa_modexpo_crt { struct CPRBX { __u16 cprb_len; /* CPRB length 220 */ __u8 cprb_ver_id; /* CPRB version id. 0x02 */ - __u8 _pad_000[3]; /* Alignment pad bytes */ + __u8 ctfm; /* Command Type Filtering Mask */ + __u8 pad_000[2]; /* Alignment pad bytes */ __u8 func_id[2]; /* function id 0x5432 */ __u8 cprb_flags[4]; /* Flags */ __u32 req_parml; /* request parameter buffer len */ From d9c2cf67b9cfd643ba85d51bc865a89a92e4f979 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 13 Feb 2023 19:38:58 +0100 Subject: [PATCH 179/182] s390/kfence: fix page fault reporting Baoquan He reported lots of KFENCE reports when /proc/kcore is read, e.g. with crash or even simpler with dd: BUG: KFENCE: invalid read in copy_from_kernel_nofault+0x5e/0x120 Invalid read at 0x00000000f4f5149f: copy_from_kernel_nofault+0x5e/0x120 read_kcore+0x6b2/0x870 proc_reg_read+0x9a/0xf0 vfs_read+0x94/0x270 ksys_read+0x70/0x100 __do_syscall+0x1d0/0x200 system_call+0x82/0xb0 The reason for this is that read_kcore() simply reads memory that might have been unmapped by KFENCE with copy_from_kernel_nofault(). Any fault due to pages being unmapped by KFENCE would be handled gracefully by the fault handler (exception table fixup). However the s390 fault handler first reports the fault, and only afterwards would perform the exception table fixup. Most architectures have this in reversed order, which also avoids the false positive KFENCE reports when an unmapped page is accessed. Therefore change the s390 fault handler so it handles exception table fixups before KFENCE page faults are reported. Reported-by: Baoquan He Tested-by: Baoquan He Acked-by: Alexander Potapenko Link: https://lore.kernel.org/r/20230213183858.1473681-1-hca@linux.ibm.com Signed-off-by: Heiko Carstens --- arch/s390/mm/fault.c | 49 +++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 65930c1928f8..a2632fd97d00 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -100,6 +100,20 @@ static enum fault_type get_fault_type(struct pt_regs *regs) return KERNEL_FAULT; } +static unsigned long get_fault_address(struct pt_regs *regs) +{ + unsigned long trans_exc_code = regs->int_parm_long; + + return trans_exc_code & __FAIL_ADDR_MASK; +} + +static bool fault_is_write(struct pt_regs *regs) +{ + unsigned long trans_exc_code = regs->int_parm_long; + + return (trans_exc_code & store_indication) == 0x400; +} + static int bad_address(void *p) { unsigned long dummy; @@ -232,15 +246,26 @@ static noinline void do_sigsegv(struct pt_regs *regs, int si_code) (void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK)); } -static noinline void do_no_context(struct pt_regs *regs) +static noinline void do_no_context(struct pt_regs *regs, vm_fault_t fault) { + enum fault_type fault_type; + unsigned long address; + bool is_write; + if (fixup_exception(regs)) return; + fault_type = get_fault_type(regs); + if ((fault_type == KERNEL_FAULT) && (fault == VM_FAULT_BADCONTEXT)) { + address = get_fault_address(regs); + is_write = fault_is_write(regs); + if (kfence_handle_page_fault(address, is_write, regs)) + return; + } /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - if (get_fault_type(regs) == KERNEL_FAULT) + if (fault_type == KERNEL_FAULT) printk(KERN_ALERT "Unable to handle kernel pointer dereference" " in virtual kernel address space\n"); else @@ -259,7 +284,7 @@ static noinline void do_low_address(struct pt_regs *regs) die (regs, "Low-address protection"); } - do_no_context(regs); + do_no_context(regs, VM_FAULT_BADACCESS); } static noinline void do_sigbus(struct pt_regs *regs) @@ -290,28 +315,28 @@ static noinline void do_fault_error(struct pt_regs *regs, vm_fault_t fault) fallthrough; case VM_FAULT_BADCONTEXT: case VM_FAULT_PFAULT: - do_no_context(regs); + do_no_context(regs, fault); break; case VM_FAULT_SIGNAL: if (!user_mode(regs)) - do_no_context(regs); + do_no_context(regs, fault); break; default: /* fault & VM_FAULT_ERROR */ if (fault & VM_FAULT_OOM) { if (!user_mode(regs)) - do_no_context(regs); + do_no_context(regs, fault); else pagefault_out_of_memory(); } else if (fault & VM_FAULT_SIGSEGV) { /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) - do_no_context(regs); + do_no_context(regs, fault); else do_sigsegv(regs, SEGV_MAPERR); } else if (fault & VM_FAULT_SIGBUS) { /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) - do_no_context(regs); + do_no_context(regs, fault); else do_sigbus(regs); } else @@ -338,7 +363,6 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access) struct mm_struct *mm; struct vm_area_struct *vma; enum fault_type type; - unsigned long trans_exc_code; unsigned long address; unsigned int flags; vm_fault_t fault; @@ -355,9 +379,8 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access) return 0; mm = tsk->mm; - trans_exc_code = regs->int_parm_long; - address = trans_exc_code & __FAIL_ADDR_MASK; - is_write = (trans_exc_code & store_indication) == 0x400; + address = get_fault_address(regs); + is_write = fault_is_write(regs); /* * Verify that the fault happened in user space, that @@ -368,8 +391,6 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access) type = get_fault_type(regs); switch (type) { case KERNEL_FAULT: - if (kfence_handle_page_fault(address, is_write, regs)) - return 0; goto out; case USER_FAULT: case GMAP_FAULT: From b977f03ec44aef7f6728bfe1a48f3ee5b0776001 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 13 Feb 2023 12:35:17 +0100 Subject: [PATCH 180/182] s390/processor: let cpu helper functions return boolean values Let cpu helper functions return boolean values. This also allows to make the code a bit simpler by getting rid of the "!!" construct. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/processor.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 53172b27a3eb..9f89d3821d16 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -54,19 +54,20 @@ static __always_inline void clear_cpu_flag(int flag) S390_lowcore.cpu_flags &= ~(1UL << flag); } -static __always_inline int test_cpu_flag(int flag) +static __always_inline bool test_cpu_flag(int flag) { - return !!(S390_lowcore.cpu_flags & (1UL << flag)); + return S390_lowcore.cpu_flags & (1UL << flag); } /* * Test CIF flag of another CPU. The caller needs to ensure that * CPU hotplug can not happen, e.g. by disabling preemption. */ -static __always_inline int test_cpu_flag_of(int flag, int cpu) +static __always_inline bool test_cpu_flag_of(int flag, int cpu) { struct lowcore *lc = lowcore_ptr[cpu]; - return !!(lc->cpu_flags & (1UL << flag)); + + return lc->cpu_flags & (1UL << flag); } #define arch_needs_cpu() test_cpu_flag(CIF_NOHZ_DELAY) From f96f41aae2b5bd34d32f462c7b45c0f4fad2b59e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 13 Feb 2023 12:35:18 +0100 Subject: [PATCH 181/182] s390/processor: add test_and_set_cpu_flag() and test_and_clear_cpu_flag() Add test_and_set_cpu_flag() and test_and_clear_cpu_flag() helper functions. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/processor.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 9f89d3821d16..e98d9650764b 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -59,6 +59,22 @@ static __always_inline bool test_cpu_flag(int flag) return S390_lowcore.cpu_flags & (1UL << flag); } +static __always_inline bool test_and_set_cpu_flag(int flag) +{ + if (test_cpu_flag(flag)) + return true; + set_cpu_flag(flag); + return false; +} + +static __always_inline bool test_and_clear_cpu_flag(int flag) +{ + if (!test_cpu_flag(flag)) + return false; + clear_cpu_flag(flag); + return true; +} + /* * Test CIF flag of another CPU. The caller needs to ensure that * CPU hotplug can not happen, e.g. by disabling preemption. From 6472a2dcc4274452bb46fb5a0d968a1c1ed772ee Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 13 Feb 2023 12:35:19 +0100 Subject: [PATCH 182/182] s390/irq,idle: simplify idle check Use the per-cpu CIF_ENABLED_WAIT flag to decide if an interrupt occurred while a cpu was idle, instead of checking two conditions within the old psw. Also move clearing of the CIF_ENABLED_WAIT bit to the early interrupt handler, which in turn makes arch_vcpu_is_preempted() also a bit more precise, since the flag is now cleared before interrupt handlers have been called. Reviewed-by: Sven Schnelle Signed-off-by: Heiko Carstens --- arch/s390/include/asm/idle.h | 1 - arch/s390/kernel/idle.c | 1 - arch/s390/kernel/irq.c | 8 ++++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h index af72a2b35758..09f763b9eb40 100644 --- a/arch/s390/include/asm/idle.h +++ b/arch/s390/include/asm/idle.h @@ -23,6 +23,5 @@ extern struct device_attribute dev_attr_idle_count; extern struct device_attribute dev_attr_idle_time_us; void psw_idle(struct s390_idle_data *data, unsigned long psw_mask); -void psw_idle_exit(void); #endif /* _S390_IDLE_H */ diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c index dd8351e76539..1a1a419ed846 100644 --- a/arch/s390/kernel/idle.c +++ b/arch/s390/kernel/idle.c @@ -28,7 +28,6 @@ void account_idle_time_irq(void) u64 cycles_new[8]; int i; - clear_cpu_flag(CIF_ENABLED_WAIT); if (smp_cpu_mtid) { stcctm(MT_DIAG, smp_cpu_mtid, cycles_new); for (i = 0; i < smp_cpu_mtid; i++) diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 45393919fe61..b020ff17d206 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -136,7 +136,7 @@ void noinstr do_io_irq(struct pt_regs *regs) { irqentry_state_t state = irqentry_enter(regs); struct pt_regs *old_regs = set_irq_regs(regs); - int from_idle; + bool from_idle; irq_enter_rcu(); @@ -146,7 +146,7 @@ void noinstr do_io_irq(struct pt_regs *regs) current->thread.last_break = regs->last_break; } - from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit; + from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT); if (from_idle) account_idle_time_irq(); @@ -171,7 +171,7 @@ void noinstr do_ext_irq(struct pt_regs *regs) { irqentry_state_t state = irqentry_enter(regs); struct pt_regs *old_regs = set_irq_regs(regs); - int from_idle; + bool from_idle; irq_enter_rcu(); @@ -185,7 +185,7 @@ void noinstr do_ext_irq(struct pt_regs *regs) regs->int_parm = S390_lowcore.ext_params; regs->int_parm_long = S390_lowcore.ext_params2; - from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit; + from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT); if (from_idle) account_idle_time_irq();