~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/s390/mm/pfault.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  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],0x258\n"
 61                 "0:     nopr    %%r7\n"
 62                 EX_TABLE(0b, 0b)
 63                 : [rc] "+d" (rc)
 64                 : [refbk] "a" (&pfault_init_refbk), "m" (pfault_init_refbk)
 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,0x258\n"
 83                 "0:     nopr    %%r7\n"
 84                 EX_TABLE(0b, 0b)
 85                 :
 86                 : [refbk] "a" (&pfault_fini_refbk), "m" (pfault_fini_refbk)
 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 is running as guest, runs a user
 97  * space process and the user space process accesses a page that the host has
 98  * paged out we get a pfault interrupt.
 99  *
100  * This allows us, within the guest, to schedule a different process. Without
101  * this mechanism the host would have to suspend the whole virtual cpu until
102  * the page has been paged in.
103  *
104  * So when we get such an interrupt then we set the state of the current task
105  * to uninterruptible and also set the need_resched flag. Both happens within
106  * interrupt context(!). If we later on want to return to user space we
107  * recognize the need_resched flag and then call schedule().  It's not very
108  * obvious how this works...
109  *
110  * Of course we have a lot of additional fun with the completion interrupt (->
111  * host signals that a page of a process has been paged in and the process can
112  * continue to run). This interrupt can arrive on any cpu and, since we have
113  * virtual cpus, actually appear before the interrupt that signals that a page
114  * is missing.
115  */
116 static void pfault_interrupt(struct ext_code ext_code,
117                              unsigned int param32, unsigned long param64)
118 {
119         struct task_struct *tsk;
120         __u16 subcode;
121         pid_t pid;
122 
123         /*
124          * Get the external interruption subcode & pfault initial/completion
125          * signal bit. VM stores this in the 'cpu address' field associated
126          * with the external interrupt.
127          */
128         subcode = ext_code.subcode;
129         if ((subcode & 0xff00) != __SUBCODE_MASK)
130                 return;
131         inc_irq_stat(IRQEXT_PFL);
132         /* Get the token (= pid of the affected task). */
133         pid = param64 & LPP_PID_MASK;
134         rcu_read_lock();
135         tsk = find_task_by_pid_ns(pid, &init_pid_ns);
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 has been swapped in by VM */
144                 if (tsk->thread.pfault_wait == 1) {
145                         /*
146                          * Initial interrupt was faster than the completion
147                          * interrupt. pfault_wait is valid. Set pfault_wait
148                          * back to zero and wake up the process. This can
149                          * safely be done because the task is still sleeping
150                          * and can't produce new pfaults.
151                          */
152                         tsk->thread.pfault_wait = 0;
153                         list_del(&tsk->thread.list);
154                         wake_up_process(tsk);
155                         put_task_struct(tsk);
156                 } else {
157                         /*
158                          * Completion interrupt was faster than initial
159                          * interrupt. Set pfault_wait to -1 so the initial
160                          * interrupt doesn't put the task to sleep.
161                          * If the task is not running, ignore the completion
162                          * interrupt since it must be a leftover of a PFAULT
163                          * CANCEL operation which didn't remove all pending
164                          * completion interrupts.
165                          */
166                         if (task_is_running(tsk))
167                                 tsk->thread.pfault_wait = -1;
168                 }
169         } else {
170                 /* signal bit not set -> a real page is missing. */
171                 if (WARN_ON_ONCE(tsk != current))
172                         goto out;
173                 if (tsk->thread.pfault_wait == 1) {
174                         /* Already on the list with a reference: put to sleep */
175                         goto block;
176                 } else if (tsk->thread.pfault_wait == -1) {
177                         /*
178                          * Completion interrupt was faster than the initial
179                          * interrupt (pfault_wait == -1). Set pfault_wait
180                          * back to zero and exit.
181                          */
182                         tsk->thread.pfault_wait = 0;
183                 } else {
184                         /*
185                          * Initial interrupt arrived before completion
186                          * interrupt. Let the task sleep.
187                          * An extra task reference is needed since a different
188                          * cpu may set the task state to TASK_RUNNING again
189                          * before the scheduler is reached.
190                          */
191                         get_task_struct(tsk);
192                         tsk->thread.pfault_wait = 1;
193                         list_add(&tsk->thread.list, &pfault_list);
194 block:
195                         /*
196                          * Since this must be a userspace fault, there
197                          * is no kernel task state to trample. Rely on the
198                          * return to userspace schedule() to block.
199                          */
200                         __set_current_state(TASK_UNINTERRUPTIBLE);
201                         set_tsk_need_resched(tsk);
202                         set_preempt_need_resched();
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, &pfault_list, list) {
217                 thread->pfault_wait = 0;
218                 list_del(&thread->list);
219                 tsk = container_of(thread, struct task_struct, thread);
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_SERVICE, pfault_interrupt);
232         if (rc)
233                 goto out_extint;
234         rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP;
235         if (rc)
236                 goto out_pfault;
237         irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
238         cpuhp_setup_state_nocalls(CPUHP_S390_PFAULT_DEAD, "s390/pfault:dead",
239                                   NULL, pfault_cpu_dead);
240         return 0;
241 
242 out_pfault:
243         unregister_external_irq(EXT_IRQ_CP_SERVICE, pfault_interrupt);
244 out_extint:
245         pfault_disable = 1;
246         return rc;
247 }
248 early_initcall(pfault_irq_init);
249 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php