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

s390: add support for transactional memory

Allow user-space processes to use transactional execution (TX).
If the TX facility is available user space programs can use
transactions for fine-grained serialization based on the data
objects that are referenced during a transaction. This is
useful for lockless data structures and speculative compiler
optimizations.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+151 -21
+1
arch/s390/include/asm/elf.h
··· 101 101 #define HWCAP_S390_HPAGE 128 102 102 #define HWCAP_S390_ETF3EH 256 103 103 #define HWCAP_S390_HIGH_GPRS 512 104 + #define HWCAP_S390_TE 1024 104 105 105 106 /* 106 107 * These are used to set parameters in the core dumps.
+5 -1
arch/s390/include/asm/lowcore.h
··· 329 329 __u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */ 330 330 __u32 access_regs_save_area[16]; /* 0x1340 */ 331 331 __u64 cregs_save_area[16]; /* 0x1380 */ 332 + __u8 pad_0x1400[0x1800-0x1400]; /* 0x1400 */ 333 + 334 + /* Transaction abort diagnostic block */ 335 + __u8 pgm_tdb[256]; /* 0x1800 */ 332 336 333 337 /* align to the top of the prefix area */ 334 - __u8 pad_0x1400[0x2000-0x1400]; /* 0x1400 */ 338 + __u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */ 335 339 } __packed; 336 340 337 341 #endif /* CONFIG_32BIT */
+6
arch/s390/include/asm/processor.h
··· 76 76 unsigned long gmap_addr; /* address of last gmap fault. */ 77 77 struct per_regs per_user; /* User specified PER registers */ 78 78 struct per_event per_event; /* Cause of the last PER trap */ 79 + unsigned long per_flags; /* Flags to control debug behavior */ 79 80 /* pfault_wait is used to block the process on a pfault event */ 80 81 unsigned long pfault_wait; 81 82 struct list_head list; 82 83 /* cpu runtime instrumentation */ 83 84 struct runtime_instr_cb *ri_cb; 84 85 int ri_signum; 86 + #ifdef CONFIG_64BIT 87 + unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ 88 + #endif 85 89 }; 90 + 91 + #define PER_FLAG_NO_TE 1UL /* Flag to disable transactions. */ 86 92 87 93 typedef struct thread_struct thread_struct; 88 94
+6 -2
arch/s390/include/asm/ptrace.h
··· 361 361 unsigned char access_id; /* PER trap access identification */ 362 362 }; 363 363 364 - #define PER_EVENT_MASK 0xE9000000UL 364 + #define PER_EVENT_MASK 0xEB000000UL 365 365 366 366 #define PER_EVENT_BRANCH 0x80000000UL 367 367 #define PER_EVENT_IFETCH 0x40000000UL 368 368 #define PER_EVENT_STORE 0x20000000UL 369 369 #define PER_EVENT_STORE_REAL 0x08000000UL 370 + #define PER_EVENT_TRANSACTION_END 0x02000000UL 370 371 #define PER_EVENT_NULLIFICATION 0x01000000UL 371 372 372 - #define PER_CONTROL_MASK 0x00a00000UL 373 + #define PER_CONTROL_MASK 0x00e00000UL 373 374 374 375 #define PER_CONTROL_BRANCH_ADDRESS 0x00800000UL 376 + #define PER_CONTROL_SUSPENSION 0x00400000UL 375 377 #define PER_CONTROL_ALTERATION 0x00200000UL 376 378 377 379 #endif ··· 487 485 #define PTRACE_GET_LAST_BREAK 0x5006 488 486 #define PTRACE_PEEK_SYSTEM_CALL 0x5007 489 487 #define PTRACE_POKE_SYSTEM_CALL 0x5008 488 + #define PTRACE_ENABLE_TE 0x5009 489 + #define PTRACE_DISABLE_TE 0x5010 490 490 491 491 /* 492 492 * PT_PROT definition is loosely based on hppa bsd definition in
+3
arch/s390/include/asm/setup.h
··· 80 80 #define MACHINE_FLAG_LPAR (1UL << 12) 81 81 #define MACHINE_FLAG_SPP (1UL << 13) 82 82 #define MACHINE_FLAG_TOPOLOGY (1UL << 14) 83 + #define MACHINE_FLAG_TE (1UL << 15) 83 84 84 85 #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) 85 86 #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) ··· 99 98 #define MACHINE_HAS_PFMF (0) 100 99 #define MACHINE_HAS_SPP (0) 101 100 #define MACHINE_HAS_TOPOLOGY (0) 101 + #define MACHINE_HAS_TE (0) 102 102 #else /* CONFIG_64BIT */ 103 103 #define MACHINE_HAS_IEEE (1) 104 104 #define MACHINE_HAS_CSP (1) ··· 111 109 #define MACHINE_HAS_PFMF (S390_lowcore.machine_flags & MACHINE_FLAG_PFMF) 112 110 #define MACHINE_HAS_SPP (S390_lowcore.machine_flags & MACHINE_FLAG_SPP) 113 111 #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) 112 + #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) 114 113 #endif /* CONFIG_64BIT */ 115 114 116 115 #define ZFCPDUMP_HSA_SIZE (32UL<<20)
+2
arch/s390/kernel/asm-offsets.c
··· 157 157 DEFINE(__LC_LAST_BREAK, offsetof(struct _lowcore, breaking_event_addr)); 158 158 DEFINE(__LC_VDSO_PER_CPU, offsetof(struct _lowcore, vdso_per_cpu_data)); 159 159 DEFINE(__LC_GMAP, offsetof(struct _lowcore, gmap)); 160 + DEFINE(__LC_PGM_TDB, offsetof(struct _lowcore, pgm_tdb)); 161 + DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb)); 160 162 DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce)); 161 163 #endif /* CONFIG_32BIT */ 162 164 return 0;
+12
arch/s390/kernel/dis.c
··· 317 317 LONG_INSN_RISBLG, 318 318 LONG_INSN_RINEXT, 319 319 LONG_INSN_RIEMIT, 320 + LONG_INSN_TABORT, 321 + LONG_INSN_TBEGIN, 322 + LONG_INSN_TBEGINC, 320 323 }; 321 324 322 325 static char *long_insn_name[] = { ··· 337 334 [LONG_INSN_RISBLG] = "risblk", 338 335 [LONG_INSN_RINEXT] = "rinext", 339 336 [LONG_INSN_RIEMIT] = "riemit", 337 + [LONG_INSN_TABORT] = "tabort", 338 + [LONG_INSN_TBEGIN] = "tbegin", 339 + [LONG_INSN_TBEGINC] = "tbeginc", 340 340 }; 341 341 342 342 static struct insn opcode[] = { ··· 615 609 { "lpswe", 0xb2, INSTR_S_RD }, 616 610 { "srnmt", 0xb9, INSTR_S_RD }, 617 611 { "lfas", 0xbd, INSTR_S_RD }, 612 + { "etndg", 0xec, INSTR_RRE_R0 }, 613 + { { 0, LONG_INSN_TABORT }, 0xfc, INSTR_S_RD }, 614 + { "tend", 0xf8, INSTR_S_RD }, 618 615 #endif 619 616 { "stidp", 0x02, INSTR_S_RD }, 620 617 { "sck", 0x04, INSTR_S_RD }, ··· 1174 1165 { "stfh", 0xcb, INSTR_RXY_RRRD }, 1175 1166 { "chf", 0xcd, INSTR_RXY_RRRD }, 1176 1167 { "clhf", 0xcf, INSTR_RXY_RRRD }, 1168 + { "ntstg", 0x25, INSTR_RXY_RRRD }, 1177 1169 #endif 1178 1170 { "lrv", 0x1e, INSTR_RXY_RRRD }, 1179 1171 { "lrvh", 0x1f, INSTR_RXY_RRRD }, ··· 1198 1188 { "mvhhi", 0x44, INSTR_SIL_RDI }, 1199 1189 { "mvhi", 0x4c, INSTR_SIL_RDI }, 1200 1190 { "mvghi", 0x48, INSTR_SIL_RDI }, 1191 + { { 0, LONG_INSN_TBEGIN }, 0x60, INSTR_SIL_RDU }, 1192 + { { 0, LONG_INSN_TBEGINC }, 0x61, INSTR_SIL_RDU }, 1201 1193 #endif 1202 1194 { "lasp", 0x00, INSTR_SSE_RDRD }, 1203 1195 { "tprot", 0x01, INSTR_SSE_RDRD },
+2 -1
arch/s390/kernel/early.c
··· 370 370 S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS; 371 371 if (test_facility(40)) 372 372 S390_lowcore.machine_flags |= MACHINE_FLAG_SPP; 373 + if (test_facility(50) && test_facility(73)) 374 + S390_lowcore.machine_flags |= MACHINE_FLAG_TE; 373 375 #endif 374 376 } 375 377 ··· 440 438 441 439 append_to_cmdline(append_ipl_scpdata); 442 440 } 443 - 444 441 445 442 /* 446 443 * Save ipl parameters, clear bss memory, initialize storage keys
+8 -4
arch/s390/kernel/entry64.S
··· 412 412 1: UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER 413 413 LAST_BREAK %r14 414 414 lg %r15,__LC_KERNEL_STACK 415 + lg %r14,__TI_task(%r12) 416 + lghi %r13,__LC_PGM_TDB 417 + tm __LC_PGM_ILC+2,0x02 # check for transaction abort 418 + jz 2f 419 + mvc __THREAD_trap_tdb(256,%r14),0(%r13) 415 420 2: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE) 416 421 la %r11,STACK_FRAME_OVERHEAD(%r15) 417 422 stmg %r0,%r7,__PT_R0(%r11) ··· 427 422 stg %r10,__PT_ARGS(%r11) 428 423 tm __LC_PGM_ILC+3,0x80 # check for per exception 429 424 jz 0f 430 - lg %r1,__TI_task(%r12) 431 425 tmhh %r8,0x0001 # kernel per event ? 432 426 jz pgm_kprobe 433 427 oi __TI_flags+7(%r12),_TIF_PER_TRAP 434 - mvc __THREAD_per_address(8,%r1),__LC_PER_ADDRESS 435 - mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE 436 - mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID 428 + mvc __THREAD_per_address(8,%r14),__LC_PER_ADDRESS 429 + mvc __THREAD_per_cause(2,%r14),__LC_PER_CAUSE 430 + mvc __THREAD_per_paid(1,%r14),__LC_PER_PAID 437 431 0: REENABLE_IRQS 438 432 xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) 439 433 larl %r1,pgm_check_table
+3 -3
arch/s390/kernel/processor.c
··· 39 39 */ 40 40 static int show_cpuinfo(struct seq_file *m, void *v) 41 41 { 42 - static const char *hwcap_str[10] = { 42 + static const char *hwcap_str[11] = { 43 43 "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", 44 - "edat", "etf3eh", "highgprs" 44 + "edat", "etf3eh", "highgprs", "te" 45 45 }; 46 46 unsigned long n = (unsigned long) v - 1; 47 47 int i; ··· 54 54 num_online_cpus(), loops_per_jiffy/(500000/HZ), 55 55 (loops_per_jiffy/(5000/HZ))%100); 56 56 seq_puts(m, "features\t: "); 57 - for (i = 0; i < 10; i++) 57 + for (i = 0; i < 11; i++) 58 58 if (hwcap_str[i] && (elf_hwcap & (1UL << i))) 59 59 seq_printf(m, "%s ", hwcap_str[i]); 60 60 seq_puts(m, "\n");
+69
arch/s390/kernel/ptrace.c
··· 42 42 REGSET_GENERAL, 43 43 REGSET_FP, 44 44 REGSET_LAST_BREAK, 45 + REGSET_TDB, 45 46 REGSET_SYSTEM_CALL, 46 47 REGSET_GENERAL_EXTENDED, 47 48 }; ··· 53 52 struct thread_struct *thread = &task->thread; 54 53 struct per_regs old, new; 55 54 55 + /* Take care of the enable/disable of transactional execution. */ 56 + if (MACHINE_HAS_TE) { 57 + unsigned long cr0, cr0_new; 58 + 59 + __ctl_store(cr0, 0, 0); 60 + /* set or clear transaction execution bits 8 and 9. */ 61 + if (task->thread.per_flags & PER_FLAG_NO_TE) 62 + cr0_new = cr0 & ~(3UL << 54); 63 + else 64 + cr0_new = cr0 | (3UL << 54); 65 + /* Only load control register 0 if necessary. */ 66 + if (cr0 != cr0_new) 67 + __ctl_load(cr0_new, 0, 0); 68 + } 69 + 56 70 /* Copy user specified PER registers */ 57 71 new.control = thread->per_user.control; 58 72 new.start = thread->per_user.start; ··· 76 60 /* merge TIF_SINGLE_STEP into user specified PER registers. */ 77 61 if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) { 78 62 new.control |= PER_EVENT_IFETCH; 63 + #ifdef CONFIG_64BIT 64 + new.control |= PER_CONTROL_SUSPENSION; 65 + new.control |= PER_EVENT_TRANSACTION_END; 66 + #endif 79 67 new.start = 0; 80 68 new.end = PSW_ADDR_INSN; 81 69 } ··· 120 100 memset(&task->thread.per_event, 0, sizeof(task->thread.per_event)); 121 101 clear_tsk_thread_flag(task, TIF_SINGLE_STEP); 122 102 clear_tsk_thread_flag(task, TIF_PER_TRAP); 103 + task->thread.per_flags = 0; 123 104 } 124 105 125 106 #ifndef CONFIG_64BIT ··· 436 415 case PTRACE_GET_LAST_BREAK: 437 416 put_user(task_thread_info(child)->last_break, 438 417 (unsigned long __user *) data); 418 + return 0; 419 + case PTRACE_ENABLE_TE: 420 + if (!MACHINE_HAS_TE) 421 + return -EIO; 422 + child->thread.per_flags &= ~PER_FLAG_NO_TE; 423 + return 0; 424 + case PTRACE_DISABLE_TE: 425 + if (!MACHINE_HAS_TE) 426 + return -EIO; 427 + child->thread.per_flags |= PER_FLAG_NO_TE; 439 428 return 0; 440 429 default: 441 430 /* Removing high order bit from addr (only for 31 bit). */ ··· 934 903 return 0; 935 904 } 936 905 906 + static int s390_tdb_get(struct task_struct *target, 907 + const struct user_regset *regset, 908 + unsigned int pos, unsigned int count, 909 + void *kbuf, void __user *ubuf) 910 + { 911 + struct pt_regs *regs = task_pt_regs(target); 912 + unsigned char *data; 913 + 914 + if (!(regs->int_code & 0x200)) 915 + return -ENODATA; 916 + data = target->thread.trap_tdb; 917 + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, data, 0, 256); 918 + } 919 + 920 + static int s390_tdb_set(struct task_struct *target, 921 + const struct user_regset *regset, 922 + unsigned int pos, unsigned int count, 923 + const void *kbuf, const void __user *ubuf) 924 + { 925 + return 0; 926 + } 927 + 937 928 #endif 938 929 939 930 static int s390_system_call_get(struct task_struct *target, ··· 1003 950 .align = sizeof(long), 1004 951 .get = s390_last_break_get, 1005 952 .set = s390_last_break_set, 953 + }, 954 + [REGSET_TDB] = { 955 + .core_note_type = NT_S390_TDB, 956 + .n = 1, 957 + .size = 256, 958 + .align = 1, 959 + .get = s390_tdb_get, 960 + .set = s390_tdb_set, 1006 961 }, 1007 962 #endif 1008 963 [REGSET_SYSTEM_CALL] = { ··· 1208 1147 .align = sizeof(long), 1209 1148 .get = s390_compat_last_break_get, 1210 1149 .set = s390_compat_last_break_set, 1150 + }, 1151 + [REGSET_TDB] = { 1152 + .core_note_type = NT_S390_TDB, 1153 + .n = 1, 1154 + .size = 256, 1155 + .align = 1, 1156 + .get = s390_tdb_get, 1157 + .set = s390_tdb_set, 1211 1158 }, 1212 1159 [REGSET_SYSTEM_CALL] = { 1213 1160 .core_note_type = NT_S390_SYSTEM_CALL,
+6
arch/s390/kernel/setup.c
··· 980 980 * HWCAP_S390_HIGH_GPRS is bit 9. 981 981 */ 982 982 elf_hwcap |= HWCAP_S390_HIGH_GPRS; 983 + 984 + /* 985 + * Transactional execution support HWCAP_S390_TE is bit 10. 986 + */ 987 + if (test_facility(50) && test_facility(73)) 988 + elf_hwcap |= HWCAP_S390_TE; 983 989 #endif 984 990 985 991 get_cpu_id(&cpu_id);
+27 -10
arch/s390/kernel/traps.c
··· 57 57 static int kstack_depth_to_print = 20; 58 58 #endif /* CONFIG_64BIT */ 59 59 60 + static inline void __user *get_trap_ip(struct pt_regs *regs) 61 + { 62 + #ifdef CONFIG_64BIT 63 + unsigned long address; 64 + 65 + if (regs->int_code & 0x200) 66 + address = *(unsigned long *)(current->thread.trap_tdb + 24); 67 + else 68 + address = regs->psw.addr; 69 + return (void __user *) 70 + ((address - (regs->int_code >> 16)) & PSW_ADDR_INSN); 71 + #else 72 + return (void __user *) 73 + ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); 74 + #endif 75 + } 76 + 60 77 /* 61 78 * For show_trace we have tree different stack to consider: 62 79 * - the panic stack which is used if the kernel stack has overflown ··· 302 285 return 1; 303 286 } 304 287 305 - static inline void __user *get_psw_address(struct pt_regs *regs) 306 - { 307 - return (void __user *) 308 - ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); 309 - } 310 - 311 288 static void __kprobes do_trap(struct pt_regs *regs, 312 289 int si_signo, int si_code, char *str) 313 290 { ··· 315 304 info.si_signo = si_signo; 316 305 info.si_errno = 0; 317 306 info.si_code = si_code; 318 - info.si_addr = get_psw_address(regs); 307 + info.si_addr = get_trap_ip(regs); 319 308 force_sig_info(si_signo, &info, current); 320 309 report_user_fault(regs, si_signo); 321 310 } else { ··· 392 381 DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, 393 382 "translation exception") 394 383 384 + #ifdef CONFIG_64BIT 385 + DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, 386 + "transaction constraint exception") 387 + #endif 388 + 395 389 static inline void do_fp_trap(struct pt_regs *regs, int fpc) 396 390 { 397 391 int si_code = 0; ··· 424 408 __u16 __user *location; 425 409 int signal = 0; 426 410 427 - location = get_psw_address(regs); 411 + location = get_trap_ip(regs); 428 412 429 413 if (user_mode(regs)) { 430 414 if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) ··· 492 476 __u16 __user *location = NULL; 493 477 int signal = 0; 494 478 495 - location = (__u16 __user *) get_psw_address(regs); 479 + location = (__u16 __user *) get_trap_ip(regs); 496 480 497 481 if (user_mode(regs)) { 498 482 get_user(*((__u16 *) opcode), location); ··· 541 525 __u16 __user *location; 542 526 int signal = 0; 543 527 544 - location = get_psw_address(regs); 528 + location = get_trap_ip(regs); 545 529 546 530 if (MACHINE_HAS_IEEE) 547 531 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); ··· 657 641 pgm_check_table[0x12] = &translation_exception; 658 642 pgm_check_table[0x13] = &special_op_exception; 659 643 #ifdef CONFIG_64BIT 644 + pgm_check_table[0x18] = &transaction_exception; 660 645 pgm_check_table[0x38] = &do_asce_exception; 661 646 pgm_check_table[0x39] = &do_dat_exception; 662 647 pgm_check_table[0x3A] = &do_dat_exception;
+1
include/linux/elf.h
··· 387 387 #define NT_S390_PREFIX 0x305 /* s390 prefix register */ 388 388 #define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ 389 389 #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ 390 + #define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ 390 391 #define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ 391 392 392 393