1 // SPDX-License-Identifier: GPL-2.0 1 2 /* 3 * Copyright IBM Corp. 1999, 2023 4 */ 5 6 #include <linux/cpuhotplug.h> 7 #include <linux/sched/task.h> 8 #include <linux/errno.h> 9 #include <linux/init.h> 10 #include <linux/irq.h> 11 #include <asm/asm-extable.h> 12 #include <asm/pfault.h> 13 #include <asm/diag.h> 14 15 #define __SUBCODE_MASK 0x0600 16 #define __PF_RES_FIELD 0x8000000000000000UL 17 18 /* 19 * 'pfault' pseudo page faults routines. 20 */ 21 static int pfault_disable; 22 23 static int __init nopfault(char *str) 24 { 25 pfault_disable = 1; 26 return 1; 27 } 28 early_param("nopfault", nopfault); 29 30 struct pfault_refbk { 31 u16 refdiagc; 32 u16 reffcode; 33 u16 refdwlen; 34 u16 refversn; 35 u64 refgaddr; 36 u64 refselmk; 37 u64 refcmpmk; 38 u64 reserved; 39 }; 40 41 static struct pfault_refbk pfault_init_refbk = 42 .refdiagc = 0x258, 43 .reffcode = 0, 44 .refdwlen = 5, 45 .refversn = 2, 46 .refgaddr = __LC_LPP, 47 .refselmk = 1UL << 48, 48 .refcmpmk = 1UL << 48, 49 .reserved = __PF_RES_FIELD 50 }; 51 52 int __pfault_init(void) 53 { 54 int rc = -EOPNOTSUPP; 55 56 if (pfault_disable) 57 return rc; 58 diag_stat_inc(DIAG_STAT_X258); 59 asm volatile( 60 " diag %[refbk],%[rc] 61 "0: nopr %%r7\n" 62 EX_TABLE(0b, 0b) 63 : [rc] "+d" (rc) 64 : [refbk] "a" (&pfault_init_re 65 : "cc"); 66 return rc; 67 } 68 69 static struct pfault_refbk pfault_fini_refbk = 70 .refdiagc = 0x258, 71 .reffcode = 1, 72 .refdwlen = 5, 73 .refversn = 2, 74 }; 75 76 void __pfault_fini(void) 77 { 78 if (pfault_disable) 79 return; 80 diag_stat_inc(DIAG_STAT_X258); 81 asm volatile( 82 " diag %[refbk],0,0x2 83 "0: nopr %%r7\n" 84 EX_TABLE(0b, 0b) 85 : 86 : [refbk] "a" (&pfault_fini_re 87 : "cc"); 88 } 89 90 static DEFINE_SPINLOCK(pfault_lock); 91 static LIST_HEAD(pfault_list); 92 93 #define PF_COMPLETE 0x0080 94 95 /* 96 * The mechanism of our pfault code: if Linux 97 * space process and the user space process ac 98 * paged out we get a pfault interrupt. 99 * 100 * This allows us, within the guest, to schedu 101 * this mechanism the host would have to suspe 102 * the page has been paged in. 103 * 104 * So when we get such an interrupt then we se 105 * to uninterruptible and also set the need_re 106 * interrupt context(!). If we later on want t 107 * recognize the need_resched flag and then ca 108 * obvious how this works... 109 * 110 * Of course we have a lot of additional fun w 111 * host signals that a page of a process has b 112 * continue to run). This interrupt can arrive 113 * virtual cpus, actually appear before the in 114 * is missing. 115 */ 116 static void pfault_interrupt(struct ext_code e 117 unsigned int para 118 { 119 struct task_struct *tsk; 120 __u16 subcode; 121 pid_t pid; 122 123 /* 124 * Get the external interruption subco 125 * signal bit. VM stores this in the ' 126 * with the external interrupt. 127 */ 128 subcode = ext_code.subcode; 129 if ((subcode & 0xff00) != __SUBCODE_MA 130 return; 131 inc_irq_stat(IRQEXT_PFL); 132 /* Get the token (= pid of the affecte 133 pid = param64 & LPP_PID_MASK; 134 rcu_read_lock(); 135 tsk = find_task_by_pid_ns(pid, &init_p 136 if (tsk) 137 get_task_struct(tsk); 138 rcu_read_unlock(); 139 if (!tsk) 140 return; 141 spin_lock(&pfault_lock); 142 if (subcode & PF_COMPLETE) { 143 /* signal bit is set -> a page 144 if (tsk->thread.pfault_wait == 145 /* 146 * Initial interrupt w 147 * interrupt. pfault_w 148 * back to zero and wa 149 * safely be done beca 150 * and can't produce n 151 */ 152 tsk->thread.pfault_wai 153 list_del(&tsk->thread. 154 wake_up_process(tsk); 155 put_task_struct(tsk); 156 } else { 157 /* 158 * Completion interrup 159 * interrupt. Set pfau 160 * interrupt doesn't p 161 * If the task is not 162 * interrupt since it 163 * CANCEL operation wh 164 * completion interrup 165 */ 166 if (task_is_running(ts 167 tsk->thread.pf 168 } 169 } else { 170 /* signal bit not set -> a rea 171 if (WARN_ON_ONCE(tsk != curren 172 goto out; 173 if (tsk->thread.pfault_wait == 174 /* Already on the list 175 goto block; 176 } else if (tsk->thread.pfault_ 177 /* 178 * Completion interrup 179 * interrupt (pfault_w 180 * back to zero and ex 181 */ 182 tsk->thread.pfault_wai 183 } else { 184 /* 185 * Initial interrupt a 186 * interrupt. Let the 187 * An extra task refer 188 * cpu may set the tas 189 * before the schedule 190 */ 191 get_task_struct(tsk); 192 tsk->thread.pfault_wai 193 list_add(&tsk->thread. 194 block: 195 /* 196 * Since this must be 197 * is no kernel task s 198 * return to userspace 199 */ 200 __set_current_state(TA 201 set_tsk_need_resched(t 202 set_preempt_need_resch 203 } 204 } 205 out: 206 spin_unlock(&pfault_lock); 207 put_task_struct(tsk); 208 } 209 210 static int pfault_cpu_dead(unsigned int cpu) 211 { 212 struct thread_struct *thread, *next; 213 struct task_struct *tsk; 214 215 spin_lock_irq(&pfault_lock); 216 list_for_each_entry_safe(thread, next, 217 thread->pfault_wait = 0; 218 list_del(&thread->list); 219 tsk = container_of(thread, str 220 wake_up_process(tsk); 221 put_task_struct(tsk); 222 } 223 spin_unlock_irq(&pfault_lock); 224 return 0; 225 } 226 227 static int __init pfault_irq_init(void) 228 { 229 int rc; 230 231 rc = register_external_irq(EXT_IRQ_CP_ 232 if (rc) 233 goto out_extint; 234 rc = pfault_init() == 0 ? 0 : -EOPNOTS 235 if (rc) 236 goto out_pfault; 237 irq_subclass_register(IRQ_SUBCLASS_SER 238 cpuhp_setup_state_nocalls(CPUHP_S390_P 239 NULL, pfault 240 return 0; 241 242 out_pfault: 243 unregister_external_irq(EXT_IRQ_CP_SER 244 out_extint: 245 pfault_disable = 1; 246 return rc; 247 } 248 early_initcall(pfault_irq_init); 249
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.