kernel - Refactor Xinvltlb and the pmap page & global tlb invalidation code
[dragonfly.git] / sys / platform / pc64 / x86_64 / pmap_inval.c
1 /*
2  * Copyright (c) 2003-2011 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 /*
36  * pmap invalidation support code.  Certain hardware requirements must
37  * be dealt with when manipulating page table entries and page directory
38  * entries within a pmap.  In particular, we cannot safely manipulate
39  * page tables which are in active use by another cpu (even if it is
40  * running in userland) for two reasons: First, TLB writebacks will
41  * race against our own modifications and tests.  Second, even if we
42  * were to use bus-locked instruction we can still screw up the 
43  * target cpu's instruction pipeline due to Intel cpu errata.
44  */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/proc.h>
50 #include <sys/vmmeter.h>
51 #include <sys/thread2.h>
52
53 #include <vm/vm.h>
54 #include <vm/pmap.h>
55 #include <vm/vm_object.h>
56
57 #include <machine/cputypes.h>
58 #include <machine/md_var.h>
59 #include <machine/specialreg.h>
60 #include <machine/smp.h>
61 #include <machine/globaldata.h>
62 #include <machine/pmap.h>
63 #include <machine/pmap_inval.h>
64
65 #if 1   /* DEBUGGING */
66 #define LOOPMASK        (/* 32 * */ 16 * 128 * 1024 - 1)
67 #endif
68
69 struct pmap_inval_info {
70         vm_offset_t     va;
71         pt_entry_t      *ptep;
72         pt_entry_t      opte;
73         pt_entry_t      npte;
74         enum { INVDONE, INVSTORE, INVCMPSET } mode;
75         int             success;
76         cpumask_t       done;
77         cpumask_t       mask;
78 #ifdef LOOPMASK
79         cpumask_t       sigmask;
80         int             failed;
81         int             xloops;
82 #endif
83 } __cachealign;
84
85 typedef struct pmap_inval_info pmap_inval_info_t;
86
87 static pmap_inval_info_t        invinfo[MAXCPU];
88 extern cpumask_t                smp_invmask;
89 #ifdef LOOPMASK
90 #ifdef LOOPMASK_IN
91 extern cpumask_t                smp_in_mask;
92 #endif
93 extern cpumask_t                smp_smurf_mask;
94 #endif
95
96 static void
97 pmap_inval_init(pmap_t pmap)
98 {
99         cpulock_t olock;
100         cpulock_t nlock;
101
102         crit_enter_id("inval");
103
104         if (pmap != &kernel_pmap) {
105                 for (;;) {
106                         olock = pmap->pm_active_lock;
107                         cpu_ccfence();
108                         nlock = olock | CPULOCK_EXCL;
109                         if (olock != nlock &&
110                             atomic_cmpset_int(&pmap->pm_active_lock,
111                                               olock, nlock)) {
112                                 break;
113                         }
114                         lwkt_process_ipiq();
115                         cpu_pause();
116                 }
117                 atomic_add_acq_long(&pmap->pm_invgen, 1);
118         }
119 }
120
121 static void
122 pmap_inval_done(pmap_t pmap)
123 {
124         if (pmap != &kernel_pmap) {
125                 atomic_clear_int(&pmap->pm_active_lock, CPULOCK_EXCL);
126                 atomic_add_acq_long(&pmap->pm_invgen, 1);
127         }
128         crit_exit_id("inval");
129 }
130
131 /*
132  * API function - invalidation the pte at (va) and replace *ptep with
133  * npte atomically across the pmap's active cpus.
134  *
135  * This is a holy mess.
136  *
137  * Returns the previous contents of *ptep.
138  */
139 static
140 void
141 loopdebug(const char *msg, pmap_inval_info_t *info)
142 {
143         int p;
144         int cpu = mycpu->gd_cpuid;
145
146         cpu_lfence();
147         atomic_add_long(&smp_smurf_mask.ary[0], 0);
148         kprintf("%s %d mode=%d m=%08jx d=%08jx s=%08jx "
149 #ifdef LOOPMASK_IN
150                 "in=%08jx "
151 #endif
152                 "smurf=%08jx\n",
153                 msg, cpu, info->mode,
154                 info->mask.ary[0],
155                 info->done.ary[0],
156                 info->sigmask.ary[0],
157 #ifdef LOOPMASK_IN
158                 smp_in_mask.ary[0],
159 #endif
160                 smp_smurf_mask.ary[0]);
161         kprintf("mdglob ");
162         for (p = 0; p < ncpus; ++p)
163                 kprintf(" %d", CPU_prvspace[p]->mdglobaldata.gd_xinvaltlb);
164         kprintf("\n");
165 }
166
167 #define CHECKSIGMASK(info)      _checksigmask(info, __FILE__, __LINE__)
168
169 static
170 void
171 _checksigmask(pmap_inval_info_t *info, const char *file, int line)
172 {
173         cpumask_t tmp;
174
175         tmp = info->mask;
176         CPUMASK_ANDMASK(tmp, info->sigmask);
177         if (CPUMASK_CMPMASKNEQ(tmp, info->mask)) {
178                 kprintf("\"%s\" line %d: bad sig/mask %08jx %08jx\n",
179                         file, line, info->sigmask.ary[0], info->mask.ary[0]);
180         }
181 }
182
183
184 pt_entry_t
185 pmap_inval_smp(pmap_t pmap, vm_offset_t va, pt_entry_t *ptep,
186                pt_entry_t npte)
187 {
188         globaldata_t gd = mycpu;
189         pmap_inval_info_t *info;
190         pt_entry_t opte;
191         int cpu = gd->gd_cpuid;
192         cpumask_t tmpmask;
193         unsigned long rflags;
194
195         /*
196          * Shortcut single-cpu case if possible.
197          */
198         if (pmap == NULL)
199                 pmap = &kernel_pmap;
200         pmap_inval_init(pmap);
201         if (CPUMASK_CMPMASKEQ(pmap->pm_active, gd->gd_cpumask)) {
202                 for (;;) {
203                         opte = *ptep;
204                         cpu_ccfence();
205                         if (atomic_cmpset_long(ptep, opte, npte)) {
206                                 if (va == (vm_offset_t)-1)
207                                         cpu_invltlb();
208                                 else
209                                         cpu_invlpg((void *)va);
210                                 pmap_inval_done(pmap);
211                                 return opte;
212                         }
213                         cpu_pause();
214                 }
215         }
216
217         /*
218          * We must wait for other cpus which may still be finishing up a
219          * prior operation.
220          */
221         info = &invinfo[cpu];
222         while (CPUMASK_TESTNZERO(info->done)) {
223 #ifdef LOOPMASK
224                 int loops;
225
226                 loops = ++info->xloops;
227                 if ((loops & LOOPMASK) == 0) {
228                         info->failed = 1;
229                         loopdebug("orig_waitA", info);
230                         /* XXX recover from possible bug */
231                         CPUMASK_ASSZERO(info->done);
232                 }
233 #endif
234                 cpu_pause();
235         }
236         KKASSERT(info->mode == INVDONE);
237
238         /*
239          * Must disable interrupts to prevent an Xinvltlb (which ignores
240          * critical sections) from trying to execute our command before we
241          * have managed to send any IPIs to the target cpus.
242          */
243         rflags = read_rflags();
244         cpu_disable_intr();
245
246         /*
247          * Must set our cpu in the invalidation scan mask before
248          * any possibility of [partial] execution (remember, XINVLTLB
249          * can interrupt a critical section).
250          */
251         if (CPUMASK_TESTBIT(smp_invmask, cpu)) {
252                 kprintf("bcpu %d already in\n", cpu);
253         }
254         ATOMIC_CPUMASK_ORBIT(smp_invmask, cpu);
255
256         info->va = va;
257         info->ptep = ptep;
258         info->npte = npte;
259         info->opte = 0;
260 #ifdef LOOPMASK
261         info->failed = 0;
262 #endif
263         tmpmask = pmap->pm_active;      /* volatile (bits may be cleared) */
264         cpu_ccfence();
265         CPUMASK_ANDMASK(tmpmask, smp_active_mask);
266         CPUMASK_ORBIT(tmpmask, cpu);
267         info->mode = INVSTORE;
268         cpu_ccfence();
269
270         /*
271          * Command may start executing the moment 'done' is initialized,
272          * disable current cpu interrupt to prevent 'done' field from
273          * changing (other cpus can't clear done bits until the originating
274          * cpu clears its mask bit, but other cpus CAN start clearing their
275          * mask bits).
276          */
277         cpu_ccfence();
278         info->mask = tmpmask;
279 #ifdef LOOPMASK
280         info->sigmask = tmpmask;
281         CHECKSIGMASK(info);
282 #endif
283         info->done = tmpmask;
284
285         /*
286          * Pass our copy of the done bits (so they don't change out from
287          * under us) to generate the Xinvltlb interrupt on the targets.
288          */
289         smp_invlpg(&tmpmask);
290         opte = info->opte;
291         KKASSERT(info->mode == INVDONE);
292
293         /*
294          * Target cpus will be in their loop exiting concurrently with our
295          * cleanup.  They will not lose the bitmask they obtained before so
296          * we can safely clear this bit.
297          */
298         ATOMIC_CPUMASK_NANDBIT(smp_invmask, cpu);
299         write_rflags(rflags);
300         pmap_inval_done(pmap);
301
302         return opte;
303 }
304
305 /*
306  * API function - invalidation the pte at (va) and replace *ptep with
307  * npte atomically only if *ptep equals opte, across the pmap's active cpus.
308  *
309  * Returns 1 on success, 0 on failure (caller typically retries).
310  */
311 int
312 pmap_inval_smp_cmpset(pmap_t pmap, vm_offset_t va, pt_entry_t *ptep,
313                       pt_entry_t opte, pt_entry_t npte)
314 {
315         globaldata_t gd = mycpu;
316         pmap_inval_info_t *info;
317         int success;
318         int cpu = gd->gd_cpuid;
319         cpumask_t tmpmask;
320         unsigned long rflags;
321
322         /*
323          * Shortcut single-cpu case if possible.
324          */
325         if (pmap == NULL)
326                 pmap = &kernel_pmap;
327         pmap_inval_init(pmap);
328         if (CPUMASK_CMPMASKEQ(pmap->pm_active, gd->gd_cpumask)) {
329                 if (atomic_cmpset_long(ptep, opte, npte)) {
330                         if (va == (vm_offset_t)-1)
331                                 cpu_invltlb();
332                         else
333                                 cpu_invlpg((void *)va);
334                         pmap_inval_done(pmap);
335                         return 1;
336                 } else {
337                         pmap_inval_done(pmap);
338                         return 0;
339                 }
340         }
341
342         /*
343          * We must wait for other cpus which may still be finishing
344          * up a prior operation.
345          */
346         info = &invinfo[cpu];
347         while (CPUMASK_TESTNZERO(info->done)) {
348 #ifdef LOOPMASK
349                 int loops;
350
351                 loops = ++info->xloops;
352                 if ((loops & LOOPMASK) == 0) {
353                         info->failed = 1;
354                         loopdebug("orig_waitB", info);
355                         /* XXX recover from possible bug */
356                         CPUMASK_ASSZERO(info->done);
357                 }
358 #endif
359                 cpu_pause();
360         }
361         KKASSERT(info->mode == INVDONE);
362
363         /*
364          * Must disable interrupts to prevent an Xinvltlb (which ignores
365          * critical sections) from trying to execute our command before we
366          * have managed to send any IPIs to the target cpus.
367          */
368         rflags = read_rflags();
369         cpu_disable_intr();
370
371         /*
372          * Must set our cpu in the invalidation scan mask before
373          * any possibility of [partial] execution (remember, XINVLTLB
374          * can interrupt a critical section).
375          */
376         if (CPUMASK_TESTBIT(smp_invmask, cpu)) {
377                 kprintf("acpu %d already in\n", cpu);
378         }
379         ATOMIC_CPUMASK_ORBIT(smp_invmask, cpu);
380
381         info->va = va;
382         info->ptep = ptep;
383         info->npte = npte;
384         info->opte = opte;
385         info->failed = 0;
386         tmpmask = pmap->pm_active;      /* volatile */
387         cpu_ccfence();
388         CPUMASK_ANDMASK(tmpmask, smp_active_mask);
389         CPUMASK_ORBIT(tmpmask, cpu);
390         info->mode = INVCMPSET;         /* initialize last */
391         info->success = 0;
392
393         /*
394          * Command may start executing the moment 'done' is initialized,
395          * disable current cpu interrupt to prevent 'done' field from
396          * changing (other cpus can't clear done bits until the originating
397          * cpu clears its mask bit).
398          */
399         cpu_ccfence();
400         info->mask = tmpmask;
401 #ifdef LOOPMASK
402         info->sigmask = tmpmask;
403         CHECKSIGMASK(info);
404 #endif
405         info->done = tmpmask;
406
407         /*
408          * Calling smp_invlpg() will issue the IPIs to XINVLTLB (which can
409          * execute even from inside a critical section), and will call us
410          * back with via pmap_inval_intr() with interrupts disabled.
411          *
412          * Unlike smp_invltlb(), this interface causes all cpus to stay
413          * inside XINVLTLB until the whole thing is done.  When our cpu
414          * detects that the whole thing is done we execute the requested
415          * operation and return.
416          */
417         smp_invlpg(&tmpmask);
418         success = info->success;
419         KKASSERT(info->mode == INVDONE);
420
421         ATOMIC_CPUMASK_NANDBIT(smp_invmask, cpu);
422         write_rflags(rflags);
423         pmap_inval_done(pmap);
424
425         return success;
426 }
427
428 /*
429  * Called with interrupts hard-disabled.
430  */
431 int
432 pmap_inval_intr(cpumask_t *cpumaskp)
433 {
434         globaldata_t gd = mycpu;
435         pmap_inval_info_t *info;
436         int loopme = 0;
437         int cpu;
438         cpumask_t cpumask;
439 #ifdef LOOPMASK
440         int loops;
441 #endif
442
443         /*
444          * Check all cpus for invalidations we may need to service.
445          */
446         cpu_ccfence();
447         cpu = gd->gd_cpuid;
448         cpumask = *cpumaskp;
449
450         while (CPUMASK_TESTNZERO(cpumask)) {
451                 int n = BSFCPUMASK(cpumask);
452
453 #ifdef LOOPMASK
454                 KKASSERT(n >= 0 && n < MAXCPU);
455 #endif
456
457                 CPUMASK_NANDBIT(cpumask, n);
458                 info = &invinfo[n];
459
460                 /*
461                  * Due to interrupts/races we can catch a new operation
462                  * in an older interrupt.  A fence is needed once we detect
463                  * the (not) done bit.
464                  */
465                 if (!CPUMASK_TESTBIT(info->done, cpu))
466                         continue;
467                 cpu_lfence();
468
469                 /*
470                  * info->mask and info->done always contain the originating
471                  * cpu until the originator is done.  Targets may still be
472                  * present in info->done after the originator is done (they
473                  * will be finishing up their loops).
474                  *
475                  * Clear info->mask bits on other cpus to indicate that they
476                  * have quiesced (entered the loop).  Once the other mask bits
477                  * are clear we can execute the operation on the original,
478                  * then clear the mask and done bits on the originator.  The
479                  * targets will then finish up their side and clear their
480                  * done bits.
481                  *
482                  * The command is considered 100% done when all done bits have
483                  * been cleared.
484                  */
485                 if (n != cpu) {
486                         /*
487                          * Command state machine for 'other' cpus.
488                          */
489                         if (CPUMASK_TESTBIT(info->mask, cpu)) {
490                                 /*
491                                  * Other cpu indicate to originator that they
492                                  * are quiesced.
493                                  */
494                                 ATOMIC_CPUMASK_NANDBIT(info->mask, cpu);
495                                 loopme = 1;
496                         } else if (CPUMASK_TESTBIT(info->mask, n)) {
497                                 /*
498                                  * Other cpu waits for originator (n) to
499                                  * complete the command.
500                                  */
501                                 loopme = 1;
502                         } else {
503                                 /*
504                                  * Other cpu detects that the originator has
505                                  * completed its command.  Now that the page
506                                  * table entry has changed, we can follow up
507                                  * with our own invalidation.
508                                  */
509                                 if (info->va == (vm_offset_t)-1)
510                                         cpu_invltlb();
511                                 else
512                                         cpu_invlpg((void *)info->va);
513                                 ATOMIC_CPUMASK_NANDBIT(info->done, cpu);
514                                 /* info invalid now */
515                                 /* loopme left alone */
516                         }
517                 } else if (CPUMASK_TESTBIT(info->mask, cpu)) {
518                         /*
519                          * Originator is waiting for other cpus
520                          */
521                         if (CPUMASK_CMPMASKNEQ(info->mask, gd->gd_cpumask)) {
522                                 /*
523                                  * Originator waits for other cpus to enter
524                                  * their loop (aka quiesce).
525                                  */
526                                 loopme = 1;
527 #ifdef LOOPMASK
528                                 loops = ++info->xloops;
529                                 if ((loops & LOOPMASK) == 0) {
530                                         info->failed = 1;
531                                         loopdebug("orig_waitC", info);
532                                         /* XXX recover from possible bug */
533                                         mdcpu->gd_xinvaltlb = 0;
534                                         smp_invlpg(&smp_active_mask);
535                                 }
536 #endif
537                         } else {
538                                 /*
539                                  * Originator executes operation and clears
540                                  * mask to allow other cpus to finish.
541                                  */
542                                 KKASSERT(info->mode != INVDONE);
543                                 if (info->mode == INVSTORE) {
544                                         info->opte = *info->ptep;
545                                         cpu_ccfence();
546                                         if (atomic_cmpset_long(info->ptep,
547                                                      info->opte, info->npte)) {
548                                                 CHECKSIGMASK(info);
549                                                 ATOMIC_CPUMASK_NANDBIT(info->mask, cpu);
550                                                 CHECKSIGMASK(info);
551                                         }
552                                         /* else will loop/retry */
553                                 } else {
554                                         if (atomic_cmpset_long(info->ptep,
555                                                               info->opte, info->npte)) {
556                                                 info->success = 1;
557                                         } else {
558                                                 info->success = 0;
559                                         }
560                                         CHECKSIGMASK(info);
561                                         ATOMIC_CPUMASK_NANDBIT(info->mask, cpu);
562                                         CHECKSIGMASK(info);
563                                 }
564                                 loopme = 1;
565                         }
566                 } else {
567                         /*
568                          * Originator does not have to wait for the other
569                          * cpus to finish.  It clears its done bit.  A new
570                          * command will not be initiated by the originator
571                          * until the other cpus have cleared their done bits
572                          * (asynchronously).
573                          */
574                         if (info->va == (vm_offset_t)-1)
575                                 cpu_invltlb();
576                         else
577                                 cpu_invlpg((void *)info->va);
578 #ifdef LOOPMASK
579                         info->xloops = 0;
580 #endif
581                         /* leave loopme alone */
582                         /* other cpus may still be finishing up */
583                         /* can't race originator since that's us */
584                         info->mode = INVDONE;
585                         ATOMIC_CPUMASK_NANDBIT(info->done, cpu);
586                 }
587         }
588         return loopme;
589 }