123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * TLB flush routines for radix kernels.
- *
- * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
- */
- #include <linux/mm.h>
- #include <linux/hugetlb.h>
- #include <linux/memblock.h>
- #include <linux/mmu_context.h>
- #include <linux/sched/mm.h>
- #include <linux/debugfs.h>
- #include <asm/ppc-opcode.h>
- #include <asm/tlb.h>
- #include <asm/tlbflush.h>
- #include <asm/trace.h>
- #include <asm/cputhreads.h>
- #include <asm/plpar_wrappers.h>
- #include "internal.h"
- /*
- * tlbiel instruction for radix, set invalidation
- * i.e., r=1 and is=01 or is=10 or is=11
- */
- static __always_inline void tlbiel_radix_set_isa300(unsigned int set, unsigned int is,
- unsigned int pid,
- unsigned int ric, unsigned int prs)
- {
- unsigned long rb;
- unsigned long rs;
- rb = (set << PPC_BITLSHIFT(51)) | (is << PPC_BITLSHIFT(53));
- rs = ((unsigned long)pid << PPC_BITLSHIFT(31));
- asm volatile(PPC_TLBIEL(%0, %1, %2, %3, 1)
- : : "r"(rb), "r"(rs), "i"(ric), "i"(prs)
- : "memory");
- }
- static void tlbiel_all_isa300(unsigned int num_sets, unsigned int is)
- {
- unsigned int set;
- asm volatile("ptesync": : :"memory");
- /*
- * Flush the first set of the TLB, and the entire Page Walk Cache
- * and partition table entries. Then flush the remaining sets of the
- * TLB.
- */
- if (early_cpu_has_feature(CPU_FTR_HVMODE)) {
- /* MSR[HV] should flush partition scope translations first. */
- tlbiel_radix_set_isa300(0, is, 0, RIC_FLUSH_ALL, 0);
- if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) {
- for (set = 1; set < num_sets; set++)
- tlbiel_radix_set_isa300(set, is, 0,
- RIC_FLUSH_TLB, 0);
- }
- }
- /* Flush process scoped entries. */
- tlbiel_radix_set_isa300(0, is, 0, RIC_FLUSH_ALL, 1);
- if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) {
- for (set = 1; set < num_sets; set++)
- tlbiel_radix_set_isa300(set, is, 0, RIC_FLUSH_TLB, 1);
- }
- ppc_after_tlbiel_barrier();
- }
- void radix__tlbiel_all(unsigned int action)
- {
- unsigned int is;
- switch (action) {
- case TLB_INVAL_SCOPE_GLOBAL:
- is = 3;
- break;
- case TLB_INVAL_SCOPE_LPID:
- is = 2;
- break;
- default:
- BUG();
- }
- if (early_cpu_has_feature(CPU_FTR_ARCH_300))
- tlbiel_all_isa300(POWER9_TLB_SETS_RADIX, is);
- else
- WARN(1, "%s called on pre-POWER9 CPU\n", __func__);
- asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT "; isync" : : :"memory");
- }
- static __always_inline void __tlbiel_pid(unsigned long pid, int set,
- unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = PPC_BIT(53); /* IS = 1 */
- rb |= set << PPC_BITLSHIFT(51);
- rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(0, 1, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbie_pid(unsigned long pid, unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = PPC_BIT(53); /* IS = 1 */
- rs = pid << PPC_BITLSHIFT(31);
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(0, 0, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbie_lpid(unsigned long lpid, unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = PPC_BIT(52); /* IS = 2 */
- rs = lpid;
- prs = 0; /* partition scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbie_lpid_guest(unsigned long lpid, unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = PPC_BIT(52); /* IS = 2 */
- rs = lpid;
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbiel_va(unsigned long va, unsigned long pid,
- unsigned long ap, unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = va & ~(PPC_BITMASK(52, 63));
- rb |= ap << PPC_BITLSHIFT(58);
- rs = pid << PPC_BITLSHIFT(31);
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(0, 1, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbie_va(unsigned long va, unsigned long pid,
- unsigned long ap, unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = va & ~(PPC_BITMASK(52, 63));
- rb |= ap << PPC_BITLSHIFT(58);
- rs = pid << PPC_BITLSHIFT(31);
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(0, 0, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbie_lpid_va(unsigned long va, unsigned long lpid,
- unsigned long ap, unsigned long ric)
- {
- unsigned long rb,rs,prs,r;
- rb = va & ~(PPC_BITMASK(52, 63));
- rb |= ap << PPC_BITLSHIFT(58);
- rs = lpid;
- prs = 0; /* partition scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
- }
- static inline void fixup_tlbie_va(unsigned long va, unsigned long pid,
- unsigned long ap)
- {
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_va(va, 0, ap, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_va(va, pid, ap, RIC_FLUSH_TLB);
- }
- }
- static inline void fixup_tlbie_va_range(unsigned long va, unsigned long pid,
- unsigned long ap)
- {
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_pid(0, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_va(va, pid, ap, RIC_FLUSH_TLB);
- }
- }
- static inline void fixup_tlbie_pid(unsigned long pid)
- {
- /*
- * We can use any address for the invalidation, pick one which is
- * probably unused as an optimisation.
- */
- unsigned long va = ((1UL << 52) - 1);
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_pid(0, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_va(va, pid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB);
- }
- }
- static inline void fixup_tlbie_lpid_va(unsigned long va, unsigned long lpid,
- unsigned long ap)
- {
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_lpid_va(va, 0, ap, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_lpid_va(va, lpid, ap, RIC_FLUSH_TLB);
- }
- }
- static inline void fixup_tlbie_lpid(unsigned long lpid)
- {
- /*
- * We can use any address for the invalidation, pick one which is
- * probably unused as an optimisation.
- */
- unsigned long va = ((1UL << 52) - 1);
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_lpid(0, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync": : :"memory");
- __tlbie_lpid_va(va, lpid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB);
- }
- }
- /*
- * We use 128 set in radix mode and 256 set in hpt mode.
- */
- static inline void _tlbiel_pid(unsigned long pid, unsigned long ric)
- {
- int set;
- asm volatile("ptesync": : :"memory");
- switch (ric) {
- case RIC_FLUSH_PWC:
- /* For PWC, only one flush is needed */
- __tlbiel_pid(pid, 0, RIC_FLUSH_PWC);
- ppc_after_tlbiel_barrier();
- return;
- case RIC_FLUSH_TLB:
- __tlbiel_pid(pid, 0, RIC_FLUSH_TLB);
- break;
- case RIC_FLUSH_ALL:
- default:
- /*
- * Flush the first set of the TLB, and if
- * we're doing a RIC_FLUSH_ALL, also flush
- * the entire Page Walk Cache.
- */
- __tlbiel_pid(pid, 0, RIC_FLUSH_ALL);
- }
- if (!cpu_has_feature(CPU_FTR_ARCH_31)) {
- /* For the remaining sets, just flush the TLB */
- for (set = 1; set < POWER9_TLB_SETS_RADIX ; set++)
- __tlbiel_pid(pid, set, RIC_FLUSH_TLB);
- }
- ppc_after_tlbiel_barrier();
- asm volatile(PPC_RADIX_INVALIDATE_ERAT_USER "; isync" : : :"memory");
- }
- static inline void _tlbie_pid(unsigned long pid, unsigned long ric)
- {
- asm volatile("ptesync": : :"memory");
- /*
- * Workaround the fact that the "ric" argument to __tlbie_pid
- * must be a compile-time constraint to match the "i" constraint
- * in the asm statement.
- */
- switch (ric) {
- case RIC_FLUSH_TLB:
- __tlbie_pid(pid, RIC_FLUSH_TLB);
- fixup_tlbie_pid(pid);
- break;
- case RIC_FLUSH_PWC:
- __tlbie_pid(pid, RIC_FLUSH_PWC);
- break;
- case RIC_FLUSH_ALL:
- default:
- __tlbie_pid(pid, RIC_FLUSH_ALL);
- fixup_tlbie_pid(pid);
- }
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- struct tlbiel_pid {
- unsigned long pid;
- unsigned long ric;
- };
- static void do_tlbiel_pid(void *info)
- {
- struct tlbiel_pid *t = info;
- if (t->ric == RIC_FLUSH_TLB)
- _tlbiel_pid(t->pid, RIC_FLUSH_TLB);
- else if (t->ric == RIC_FLUSH_PWC)
- _tlbiel_pid(t->pid, RIC_FLUSH_PWC);
- else
- _tlbiel_pid(t->pid, RIC_FLUSH_ALL);
- }
- static inline void _tlbiel_pid_multicast(struct mm_struct *mm,
- unsigned long pid, unsigned long ric)
- {
- struct cpumask *cpus = mm_cpumask(mm);
- struct tlbiel_pid t = { .pid = pid, .ric = ric };
- on_each_cpu_mask(cpus, do_tlbiel_pid, &t, 1);
- /*
- * Always want the CPU translations to be invalidated with tlbiel in
- * these paths, so while coprocessors must use tlbie, we can not
- * optimise away the tlbiel component.
- */
- if (atomic_read(&mm->context.copros) > 0)
- _tlbie_pid(pid, RIC_FLUSH_ALL);
- }
- static inline void _tlbie_lpid(unsigned long lpid, unsigned long ric)
- {
- asm volatile("ptesync": : :"memory");
- /*
- * Workaround the fact that the "ric" argument to __tlbie_pid
- * must be a compile-time contraint to match the "i" constraint
- * in the asm statement.
- */
- switch (ric) {
- case RIC_FLUSH_TLB:
- __tlbie_lpid(lpid, RIC_FLUSH_TLB);
- fixup_tlbie_lpid(lpid);
- break;
- case RIC_FLUSH_PWC:
- __tlbie_lpid(lpid, RIC_FLUSH_PWC);
- break;
- case RIC_FLUSH_ALL:
- default:
- __tlbie_lpid(lpid, RIC_FLUSH_ALL);
- fixup_tlbie_lpid(lpid);
- }
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- static __always_inline void _tlbie_lpid_guest(unsigned long lpid, unsigned long ric)
- {
- /*
- * Workaround the fact that the "ric" argument to __tlbie_pid
- * must be a compile-time contraint to match the "i" constraint
- * in the asm statement.
- */
- switch (ric) {
- case RIC_FLUSH_TLB:
- __tlbie_lpid_guest(lpid, RIC_FLUSH_TLB);
- break;
- case RIC_FLUSH_PWC:
- __tlbie_lpid_guest(lpid, RIC_FLUSH_PWC);
- break;
- case RIC_FLUSH_ALL:
- default:
- __tlbie_lpid_guest(lpid, RIC_FLUSH_ALL);
- }
- fixup_tlbie_lpid(lpid);
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- static inline void __tlbiel_va_range(unsigned long start, unsigned long end,
- unsigned long pid, unsigned long page_size,
- unsigned long psize)
- {
- unsigned long addr;
- unsigned long ap = mmu_get_ap(psize);
- for (addr = start; addr < end; addr += page_size)
- __tlbiel_va(addr, pid, ap, RIC_FLUSH_TLB);
- }
- static __always_inline void _tlbiel_va(unsigned long va, unsigned long pid,
- unsigned long psize, unsigned long ric)
- {
- unsigned long ap = mmu_get_ap(psize);
- asm volatile("ptesync": : :"memory");
- __tlbiel_va(va, pid, ap, ric);
- ppc_after_tlbiel_barrier();
- }
- static inline void _tlbiel_va_range(unsigned long start, unsigned long end,
- unsigned long pid, unsigned long page_size,
- unsigned long psize, bool also_pwc)
- {
- asm volatile("ptesync": : :"memory");
- if (also_pwc)
- __tlbiel_pid(pid, 0, RIC_FLUSH_PWC);
- __tlbiel_va_range(start, end, pid, page_size, psize);
- ppc_after_tlbiel_barrier();
- }
- static inline void __tlbie_va_range(unsigned long start, unsigned long end,
- unsigned long pid, unsigned long page_size,
- unsigned long psize)
- {
- unsigned long addr;
- unsigned long ap = mmu_get_ap(psize);
- for (addr = start; addr < end; addr += page_size)
- __tlbie_va(addr, pid, ap, RIC_FLUSH_TLB);
- fixup_tlbie_va_range(addr - page_size, pid, ap);
- }
- static __always_inline void _tlbie_va(unsigned long va, unsigned long pid,
- unsigned long psize, unsigned long ric)
- {
- unsigned long ap = mmu_get_ap(psize);
- asm volatile("ptesync": : :"memory");
- __tlbie_va(va, pid, ap, ric);
- fixup_tlbie_va(va, pid, ap);
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- struct tlbiel_va {
- unsigned long pid;
- unsigned long va;
- unsigned long psize;
- unsigned long ric;
- };
- static void do_tlbiel_va(void *info)
- {
- struct tlbiel_va *t = info;
- if (t->ric == RIC_FLUSH_TLB)
- _tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_TLB);
- else if (t->ric == RIC_FLUSH_PWC)
- _tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_PWC);
- else
- _tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_ALL);
- }
- static inline void _tlbiel_va_multicast(struct mm_struct *mm,
- unsigned long va, unsigned long pid,
- unsigned long psize, unsigned long ric)
- {
- struct cpumask *cpus = mm_cpumask(mm);
- struct tlbiel_va t = { .va = va, .pid = pid, .psize = psize, .ric = ric };
- on_each_cpu_mask(cpus, do_tlbiel_va, &t, 1);
- if (atomic_read(&mm->context.copros) > 0)
- _tlbie_va(va, pid, psize, RIC_FLUSH_TLB);
- }
- struct tlbiel_va_range {
- unsigned long pid;
- unsigned long start;
- unsigned long end;
- unsigned long page_size;
- unsigned long psize;
- bool also_pwc;
- };
- static void do_tlbiel_va_range(void *info)
- {
- struct tlbiel_va_range *t = info;
- _tlbiel_va_range(t->start, t->end, t->pid, t->page_size,
- t->psize, t->also_pwc);
- }
- static __always_inline void _tlbie_lpid_va(unsigned long va, unsigned long lpid,
- unsigned long psize, unsigned long ric)
- {
- unsigned long ap = mmu_get_ap(psize);
- asm volatile("ptesync": : :"memory");
- __tlbie_lpid_va(va, lpid, ap, ric);
- fixup_tlbie_lpid_va(va, lpid, ap);
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- static inline void _tlbie_va_range(unsigned long start, unsigned long end,
- unsigned long pid, unsigned long page_size,
- unsigned long psize, bool also_pwc)
- {
- asm volatile("ptesync": : :"memory");
- if (also_pwc)
- __tlbie_pid(pid, RIC_FLUSH_PWC);
- __tlbie_va_range(start, end, pid, page_size, psize);
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- static inline void _tlbiel_va_range_multicast(struct mm_struct *mm,
- unsigned long start, unsigned long end,
- unsigned long pid, unsigned long page_size,
- unsigned long psize, bool also_pwc)
- {
- struct cpumask *cpus = mm_cpumask(mm);
- struct tlbiel_va_range t = { .start = start, .end = end,
- .pid = pid, .page_size = page_size,
- .psize = psize, .also_pwc = also_pwc };
- on_each_cpu_mask(cpus, do_tlbiel_va_range, &t, 1);
- if (atomic_read(&mm->context.copros) > 0)
- _tlbie_va_range(start, end, pid, page_size, psize, also_pwc);
- }
- /*
- * Base TLB flushing operations:
- *
- * - flush_tlb_mm(mm) flushes the specified mm context TLB's
- * - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
- * - flush_tlb_kernel_range(start, end) flushes kernel pages
- *
- * - local_* variants of page and mm only apply to the current
- * processor
- */
- void radix__local_flush_tlb_mm(struct mm_struct *mm)
- {
- unsigned long pid;
- preempt_disable();
- pid = mm->context.id;
- if (pid != MMU_NO_CONTEXT)
- _tlbiel_pid(pid, RIC_FLUSH_TLB);
- preempt_enable();
- }
- EXPORT_SYMBOL(radix__local_flush_tlb_mm);
- #ifndef CONFIG_SMP
- void radix__local_flush_all_mm(struct mm_struct *mm)
- {
- unsigned long pid;
- preempt_disable();
- pid = mm->context.id;
- if (pid != MMU_NO_CONTEXT)
- _tlbiel_pid(pid, RIC_FLUSH_ALL);
- preempt_enable();
- }
- EXPORT_SYMBOL(radix__local_flush_all_mm);
- static void __flush_all_mm(struct mm_struct *mm, bool fullmm)
- {
- radix__local_flush_all_mm(mm);
- }
- #endif /* CONFIG_SMP */
- void radix__local_flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
- int psize)
- {
- unsigned long pid;
- preempt_disable();
- pid = mm->context.id;
- if (pid != MMU_NO_CONTEXT)
- _tlbiel_va(vmaddr, pid, psize, RIC_FLUSH_TLB);
- preempt_enable();
- }
- void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
- {
- #ifdef CONFIG_HUGETLB_PAGE
- /* need the return fix for nohash.c */
- if (is_vm_hugetlb_page(vma))
- return radix__local_flush_hugetlb_page(vma, vmaddr);
- #endif
- radix__local_flush_tlb_page_psize(vma->vm_mm, vmaddr, mmu_virtual_psize);
- }
- EXPORT_SYMBOL(radix__local_flush_tlb_page);
- static bool mm_needs_flush_escalation(struct mm_struct *mm)
- {
- /*
- * The P9 nest MMU has issues with the page walk cache caching PTEs
- * and not flushing them when RIC = 0 for a PID/LPID invalidate.
- *
- * This may have been fixed in shipping firmware (by disabling PWC
- * or preventing it from caching PTEs), but until that is confirmed,
- * this workaround is required - escalate all RIC=0 IS=1/2/3 flushes
- * to RIC=2.
- *
- * POWER10 (and P9P) does not have this problem.
- */
- if (cpu_has_feature(CPU_FTR_ARCH_31))
- return false;
- if (atomic_read(&mm->context.copros) > 0)
- return true;
- return false;
- }
- /*
- * If always_flush is true, then flush even if this CPU can't be removed
- * from mm_cpumask.
- */
- void exit_lazy_flush_tlb(struct mm_struct *mm, bool always_flush)
- {
- unsigned long pid = mm->context.id;
- int cpu = smp_processor_id();
- /*
- * A kthread could have done a mmget_not_zero() after the flushing CPU
- * checked mm_cpumask, and be in the process of kthread_use_mm when
- * interrupted here. In that case, current->mm will be set to mm,
- * because kthread_use_mm() setting ->mm and switching to the mm is
- * done with interrupts off.
- */
- if (current->mm == mm)
- goto out;
- if (current->active_mm == mm) {
- WARN_ON_ONCE(current->mm != NULL);
- /* Is a kernel thread and is using mm as the lazy tlb */
- mmgrab(&init_mm);
- current->active_mm = &init_mm;
- switch_mm_irqs_off(mm, &init_mm, current);
- mmdrop(mm);
- }
- /*
- * This IPI may be initiated from any source including those not
- * running the mm, so there may be a racing IPI that comes after
- * this one which finds the cpumask already clear. Check and avoid
- * underflowing the active_cpus count in that case. The race should
- * not otherwise be a problem, but the TLB must be flushed because
- * that's what the caller expects.
- */
- if (cpumask_test_cpu(cpu, mm_cpumask(mm))) {
- atomic_dec(&mm->context.active_cpus);
- cpumask_clear_cpu(cpu, mm_cpumask(mm));
- always_flush = true;
- }
- out:
- if (always_flush)
- _tlbiel_pid(pid, RIC_FLUSH_ALL);
- }
- #ifdef CONFIG_SMP
- static void do_exit_flush_lazy_tlb(void *arg)
- {
- struct mm_struct *mm = arg;
- exit_lazy_flush_tlb(mm, true);
- }
- static void exit_flush_lazy_tlbs(struct mm_struct *mm)
- {
- /*
- * Would be nice if this was async so it could be run in
- * parallel with our local flush, but generic code does not
- * give a good API for it. Could extend the generic code or
- * make a special powerpc IPI for flushing TLBs.
- * For now it's not too performance critical.
- */
- smp_call_function_many(mm_cpumask(mm), do_exit_flush_lazy_tlb,
- (void *)mm, 1);
- }
- #else /* CONFIG_SMP */
- static inline void exit_flush_lazy_tlbs(struct mm_struct *mm) { }
- #endif /* CONFIG_SMP */
- static DEFINE_PER_CPU(unsigned int, mm_cpumask_trim_clock);
- /*
- * Interval between flushes at which we send out IPIs to check whether the
- * mm_cpumask can be trimmed for the case where it's not a single-threaded
- * process flushing its own mm. The intent is to reduce the cost of later
- * flushes. Don't want this to be so low that it adds noticable cost to TLB
- * flushing, or so high that it doesn't help reduce global TLBIEs.
- */
- static unsigned long tlb_mm_cpumask_trim_timer = 1073;
- static bool tick_and_test_trim_clock(void)
- {
- if (__this_cpu_inc_return(mm_cpumask_trim_clock) ==
- tlb_mm_cpumask_trim_timer) {
- __this_cpu_write(mm_cpumask_trim_clock, 0);
- return true;
- }
- return false;
- }
- enum tlb_flush_type {
- FLUSH_TYPE_NONE,
- FLUSH_TYPE_LOCAL,
- FLUSH_TYPE_GLOBAL,
- };
- static enum tlb_flush_type flush_type_needed(struct mm_struct *mm, bool fullmm)
- {
- int active_cpus = atomic_read(&mm->context.active_cpus);
- int cpu = smp_processor_id();
- if (active_cpus == 0)
- return FLUSH_TYPE_NONE;
- if (active_cpus == 1 && cpumask_test_cpu(cpu, mm_cpumask(mm))) {
- if (current->mm != mm) {
- /*
- * Asynchronous flush sources may trim down to nothing
- * if the process is not running, so occasionally try
- * to trim.
- */
- if (tick_and_test_trim_clock()) {
- exit_lazy_flush_tlb(mm, true);
- return FLUSH_TYPE_NONE;
- }
- }
- return FLUSH_TYPE_LOCAL;
- }
- /* Coprocessors require TLBIE to invalidate nMMU. */
- if (atomic_read(&mm->context.copros) > 0)
- return FLUSH_TYPE_GLOBAL;
- /*
- * In the fullmm case there's no point doing the exit_flush_lazy_tlbs
- * because the mm is being taken down anyway, and a TLBIE tends to
- * be faster than an IPI+TLBIEL.
- */
- if (fullmm)
- return FLUSH_TYPE_GLOBAL;
- /*
- * If we are running the only thread of a single-threaded process,
- * then we should almost always be able to trim off the rest of the
- * CPU mask (except in the case of use_mm() races), so always try
- * trimming the mask.
- */
- if (atomic_read(&mm->mm_users) <= 1 && current->mm == mm) {
- exit_flush_lazy_tlbs(mm);
- /*
- * use_mm() race could prevent IPIs from being able to clear
- * the cpumask here, however those users are established
- * after our first check (and so after the PTEs are removed),
- * and the TLB still gets flushed by the IPI, so this CPU
- * will only require a local flush.
- */
- return FLUSH_TYPE_LOCAL;
- }
- /*
- * Occasionally try to trim down the cpumask. It's possible this can
- * bring the mask to zero, which results in no flush.
- */
- if (tick_and_test_trim_clock()) {
- exit_flush_lazy_tlbs(mm);
- if (current->mm == mm)
- return FLUSH_TYPE_LOCAL;
- if (cpumask_test_cpu(cpu, mm_cpumask(mm)))
- exit_lazy_flush_tlb(mm, true);
- return FLUSH_TYPE_NONE;
- }
- return FLUSH_TYPE_GLOBAL;
- }
- #ifdef CONFIG_SMP
- void radix__flush_tlb_mm(struct mm_struct *mm)
- {
- unsigned long pid;
- enum tlb_flush_type type;
- pid = mm->context.id;
- if (unlikely(pid == MMU_NO_CONTEXT))
- return;
- preempt_disable();
- /*
- * Order loads of mm_cpumask (in flush_type_needed) vs previous
- * stores to clear ptes before the invalidate. See barrier in
- * switch_mm_irqs_off
- */
- smp_mb();
- type = flush_type_needed(mm, false);
- if (type == FLUSH_TYPE_LOCAL) {
- _tlbiel_pid(pid, RIC_FLUSH_TLB);
- } else if (type == FLUSH_TYPE_GLOBAL) {
- if (!mmu_has_feature(MMU_FTR_GTSE)) {
- unsigned long tgt = H_RPTI_TARGET_CMMU;
- if (atomic_read(&mm->context.copros) > 0)
- tgt |= H_RPTI_TARGET_NMMU;
- pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB,
- H_RPTI_PAGE_ALL, 0, -1UL);
- } else if (cputlb_use_tlbie()) {
- if (mm_needs_flush_escalation(mm))
- _tlbie_pid(pid, RIC_FLUSH_ALL);
- else
- _tlbie_pid(pid, RIC_FLUSH_TLB);
- } else {
- _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_TLB);
- }
- }
- preempt_enable();
- }
- EXPORT_SYMBOL(radix__flush_tlb_mm);
- static void __flush_all_mm(struct mm_struct *mm, bool fullmm)
- {
- unsigned long pid;
- enum tlb_flush_type type;
- pid = mm->context.id;
- if (unlikely(pid == MMU_NO_CONTEXT))
- return;
- preempt_disable();
- smp_mb(); /* see radix__flush_tlb_mm */
- type = flush_type_needed(mm, fullmm);
- if (type == FLUSH_TYPE_LOCAL) {
- _tlbiel_pid(pid, RIC_FLUSH_ALL);
- } else if (type == FLUSH_TYPE_GLOBAL) {
- if (!mmu_has_feature(MMU_FTR_GTSE)) {
- unsigned long tgt = H_RPTI_TARGET_CMMU;
- unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC |
- H_RPTI_TYPE_PRT;
- if (atomic_read(&mm->context.copros) > 0)
- tgt |= H_RPTI_TARGET_NMMU;
- pseries_rpt_invalidate(pid, tgt, type,
- H_RPTI_PAGE_ALL, 0, -1UL);
- } else if (cputlb_use_tlbie())
- _tlbie_pid(pid, RIC_FLUSH_ALL);
- else
- _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL);
- }
- preempt_enable();
- }
- void radix__flush_all_mm(struct mm_struct *mm)
- {
- __flush_all_mm(mm, false);
- }
- EXPORT_SYMBOL(radix__flush_all_mm);
- void radix__flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
- int psize)
- {
- unsigned long pid;
- enum tlb_flush_type type;
- pid = mm->context.id;
- if (unlikely(pid == MMU_NO_CONTEXT))
- return;
- preempt_disable();
- smp_mb(); /* see radix__flush_tlb_mm */
- type = flush_type_needed(mm, false);
- if (type == FLUSH_TYPE_LOCAL) {
- _tlbiel_va(vmaddr, pid, psize, RIC_FLUSH_TLB);
- } else if (type == FLUSH_TYPE_GLOBAL) {
- if (!mmu_has_feature(MMU_FTR_GTSE)) {
- unsigned long tgt, pg_sizes, size;
- tgt = H_RPTI_TARGET_CMMU;
- pg_sizes = psize_to_rpti_pgsize(psize);
- size = 1UL << mmu_psize_to_shift(psize);
- if (atomic_read(&mm->context.copros) > 0)
- tgt |= H_RPTI_TARGET_NMMU;
- pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB,
- pg_sizes, vmaddr,
- vmaddr + size);
- } else if (cputlb_use_tlbie())
- _tlbie_va(vmaddr, pid, psize, RIC_FLUSH_TLB);
- else
- _tlbiel_va_multicast(mm, vmaddr, pid, psize, RIC_FLUSH_TLB);
- }
- preempt_enable();
- }
- void radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
- {
- #ifdef CONFIG_HUGETLB_PAGE
- if (is_vm_hugetlb_page(vma))
- return radix__flush_hugetlb_page(vma, vmaddr);
- #endif
- radix__flush_tlb_page_psize(vma->vm_mm, vmaddr, mmu_virtual_psize);
- }
- EXPORT_SYMBOL(radix__flush_tlb_page);
- #endif /* CONFIG_SMP */
- static void do_tlbiel_kernel(void *info)
- {
- _tlbiel_pid(0, RIC_FLUSH_ALL);
- }
- static inline void _tlbiel_kernel_broadcast(void)
- {
- on_each_cpu(do_tlbiel_kernel, NULL, 1);
- if (tlbie_capable) {
- /*
- * Coherent accelerators don't refcount kernel memory mappings,
- * so have to always issue a tlbie for them. This is quite a
- * slow path anyway.
- */
- _tlbie_pid(0, RIC_FLUSH_ALL);
- }
- }
- /*
- * If kernel TLBIs ever become local rather than global, then
- * drivers/misc/ocxl/link.c:ocxl_link_add_pe will need some work, as it
- * assumes kernel TLBIs are global.
- */
- void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end)
- {
- if (!mmu_has_feature(MMU_FTR_GTSE)) {
- unsigned long tgt = H_RPTI_TARGET_CMMU | H_RPTI_TARGET_NMMU;
- unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC |
- H_RPTI_TYPE_PRT;
- pseries_rpt_invalidate(0, tgt, type, H_RPTI_PAGE_ALL,
- start, end);
- } else if (cputlb_use_tlbie())
- _tlbie_pid(0, RIC_FLUSH_ALL);
- else
- _tlbiel_kernel_broadcast();
- }
- EXPORT_SYMBOL(radix__flush_tlb_kernel_range);
- #define TLB_FLUSH_ALL -1UL
- /*
- * Number of pages above which we invalidate the entire PID rather than
- * flush individual pages, for local and global flushes respectively.
- *
- * tlbie goes out to the interconnect and individual ops are more costly.
- * It also does not iterate over sets like the local tlbiel variant when
- * invalidating a full PID, so it has a far lower threshold to change from
- * individual page flushes to full-pid flushes.
- */
- static u32 tlb_single_page_flush_ceiling __read_mostly = 33;
- static u32 tlb_local_single_page_flush_ceiling __read_mostly = POWER9_TLB_SETS_RADIX * 2;
- static inline void __radix__flush_tlb_range(struct mm_struct *mm,
- unsigned long start, unsigned long end)
- {
- unsigned long pid;
- unsigned int page_shift = mmu_psize_defs[mmu_virtual_psize].shift;
- unsigned long page_size = 1UL << page_shift;
- unsigned long nr_pages = (end - start) >> page_shift;
- bool fullmm = (end == TLB_FLUSH_ALL);
- bool flush_pid, flush_pwc = false;
- enum tlb_flush_type type;
- pid = mm->context.id;
- if (unlikely(pid == MMU_NO_CONTEXT))
- return;
- preempt_disable();
- smp_mb(); /* see radix__flush_tlb_mm */
- type = flush_type_needed(mm, fullmm);
- if (type == FLUSH_TYPE_NONE)
- goto out;
- if (fullmm)
- flush_pid = true;
- else if (type == FLUSH_TYPE_GLOBAL)
- flush_pid = nr_pages > tlb_single_page_flush_ceiling;
- else
- flush_pid = nr_pages > tlb_local_single_page_flush_ceiling;
- /*
- * full pid flush already does the PWC flush. if it is not full pid
- * flush check the range is more than PMD and force a pwc flush
- * mremap() depends on this behaviour.
- */
- if (!flush_pid && (end - start) >= PMD_SIZE)
- flush_pwc = true;
- if (!mmu_has_feature(MMU_FTR_GTSE) && type == FLUSH_TYPE_GLOBAL) {
- unsigned long type = H_RPTI_TYPE_TLB;
- unsigned long tgt = H_RPTI_TARGET_CMMU;
- unsigned long pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize);
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
- pg_sizes |= psize_to_rpti_pgsize(MMU_PAGE_2M);
- if (atomic_read(&mm->context.copros) > 0)
- tgt |= H_RPTI_TARGET_NMMU;
- if (flush_pwc)
- type |= H_RPTI_TYPE_PWC;
- pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end);
- } else if (flush_pid) {
- /*
- * We are now flushing a range larger than PMD size force a RIC_FLUSH_ALL
- */
- if (type == FLUSH_TYPE_LOCAL) {
- _tlbiel_pid(pid, RIC_FLUSH_ALL);
- } else {
- if (cputlb_use_tlbie()) {
- _tlbie_pid(pid, RIC_FLUSH_ALL);
- } else {
- _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL);
- }
- }
- } else {
- bool hflush;
- unsigned long hstart, hend;
- hstart = (start + PMD_SIZE - 1) & PMD_MASK;
- hend = end & PMD_MASK;
- hflush = IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && hstart < hend;
- if (type == FLUSH_TYPE_LOCAL) {
- asm volatile("ptesync": : :"memory");
- if (flush_pwc)
- /* For PWC, only one flush is needed */
- __tlbiel_pid(pid, 0, RIC_FLUSH_PWC);
- __tlbiel_va_range(start, end, pid, page_size, mmu_virtual_psize);
- if (hflush)
- __tlbiel_va_range(hstart, hend, pid,
- PMD_SIZE, MMU_PAGE_2M);
- ppc_after_tlbiel_barrier();
- } else if (cputlb_use_tlbie()) {
- asm volatile("ptesync": : :"memory");
- if (flush_pwc)
- __tlbie_pid(pid, RIC_FLUSH_PWC);
- __tlbie_va_range(start, end, pid, page_size, mmu_virtual_psize);
- if (hflush)
- __tlbie_va_range(hstart, hend, pid,
- PMD_SIZE, MMU_PAGE_2M);
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- } else {
- _tlbiel_va_range_multicast(mm,
- start, end, pid, page_size, mmu_virtual_psize, flush_pwc);
- if (hflush)
- _tlbiel_va_range_multicast(mm,
- hstart, hend, pid, PMD_SIZE, MMU_PAGE_2M, flush_pwc);
- }
- }
- out:
- preempt_enable();
- }
- void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
- {
- #ifdef CONFIG_HUGETLB_PAGE
- if (is_vm_hugetlb_page(vma))
- return radix__flush_hugetlb_tlb_range(vma, start, end);
- #endif
- __radix__flush_tlb_range(vma->vm_mm, start, end);
- }
- EXPORT_SYMBOL(radix__flush_tlb_range);
- static int radix_get_mmu_psize(int page_size)
- {
- int psize;
- if (page_size == (1UL << mmu_psize_defs[mmu_virtual_psize].shift))
- psize = mmu_virtual_psize;
- else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_2M].shift))
- psize = MMU_PAGE_2M;
- else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_1G].shift))
- psize = MMU_PAGE_1G;
- else
- return -1;
- return psize;
- }
- /*
- * Flush partition scoped LPID address translation for all CPUs.
- */
- void radix__flush_tlb_lpid_page(unsigned int lpid,
- unsigned long addr,
- unsigned long page_size)
- {
- int psize = radix_get_mmu_psize(page_size);
- _tlbie_lpid_va(addr, lpid, psize, RIC_FLUSH_TLB);
- }
- EXPORT_SYMBOL_GPL(radix__flush_tlb_lpid_page);
- /*
- * Flush partition scoped PWC from LPID for all CPUs.
- */
- void radix__flush_pwc_lpid(unsigned int lpid)
- {
- _tlbie_lpid(lpid, RIC_FLUSH_PWC);
- }
- EXPORT_SYMBOL_GPL(radix__flush_pwc_lpid);
- /*
- * Flush partition scoped translations from LPID (=LPIDR)
- */
- void radix__flush_all_lpid(unsigned int lpid)
- {
- _tlbie_lpid(lpid, RIC_FLUSH_ALL);
- }
- EXPORT_SYMBOL_GPL(radix__flush_all_lpid);
- /*
- * Flush process scoped translations from LPID (=LPIDR)
- */
- void radix__flush_all_lpid_guest(unsigned int lpid)
- {
- _tlbie_lpid_guest(lpid, RIC_FLUSH_ALL);
- }
- void radix__tlb_flush(struct mmu_gather *tlb)
- {
- int psize = 0;
- struct mm_struct *mm = tlb->mm;
- int page_size = tlb->page_size;
- unsigned long start = tlb->start;
- unsigned long end = tlb->end;
- /*
- * if page size is not something we understand, do a full mm flush
- *
- * A "fullmm" flush must always do a flush_all_mm (RIC=2) flush
- * that flushes the process table entry cache upon process teardown.
- * See the comment for radix in arch_exit_mmap().
- */
- if (tlb->fullmm || tlb->need_flush_all) {
- __flush_all_mm(mm, true);
- } else if ( (psize = radix_get_mmu_psize(page_size)) == -1) {
- if (!tlb->freed_tables)
- radix__flush_tlb_mm(mm);
- else
- radix__flush_all_mm(mm);
- } else {
- if (!tlb->freed_tables)
- radix__flush_tlb_range_psize(mm, start, end, psize);
- else
- radix__flush_tlb_pwc_range_psize(mm, start, end, psize);
- }
- }
- static void __radix__flush_tlb_range_psize(struct mm_struct *mm,
- unsigned long start, unsigned long end,
- int psize, bool also_pwc)
- {
- unsigned long pid;
- unsigned int page_shift = mmu_psize_defs[psize].shift;
- unsigned long page_size = 1UL << page_shift;
- unsigned long nr_pages = (end - start) >> page_shift;
- bool fullmm = (end == TLB_FLUSH_ALL);
- bool flush_pid;
- enum tlb_flush_type type;
- pid = mm->context.id;
- if (unlikely(pid == MMU_NO_CONTEXT))
- return;
- fullmm = (end == TLB_FLUSH_ALL);
- preempt_disable();
- smp_mb(); /* see radix__flush_tlb_mm */
- type = flush_type_needed(mm, fullmm);
- if (type == FLUSH_TYPE_NONE)
- goto out;
- if (fullmm)
- flush_pid = true;
- else if (type == FLUSH_TYPE_GLOBAL)
- flush_pid = nr_pages > tlb_single_page_flush_ceiling;
- else
- flush_pid = nr_pages > tlb_local_single_page_flush_ceiling;
- if (!mmu_has_feature(MMU_FTR_GTSE) && type == FLUSH_TYPE_GLOBAL) {
- unsigned long tgt = H_RPTI_TARGET_CMMU;
- unsigned long type = H_RPTI_TYPE_TLB;
- unsigned long pg_sizes = psize_to_rpti_pgsize(psize);
- if (also_pwc)
- type |= H_RPTI_TYPE_PWC;
- if (atomic_read(&mm->context.copros) > 0)
- tgt |= H_RPTI_TARGET_NMMU;
- pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end);
- } else if (flush_pid) {
- if (type == FLUSH_TYPE_LOCAL) {
- _tlbiel_pid(pid, also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB);
- } else {
- if (cputlb_use_tlbie()) {
- if (mm_needs_flush_escalation(mm))
- also_pwc = true;
- _tlbie_pid(pid,
- also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB);
- } else {
- _tlbiel_pid_multicast(mm, pid,
- also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB);
- }
- }
- } else {
- if (type == FLUSH_TYPE_LOCAL)
- _tlbiel_va_range(start, end, pid, page_size, psize, also_pwc);
- else if (cputlb_use_tlbie())
- _tlbie_va_range(start, end, pid, page_size, psize, also_pwc);
- else
- _tlbiel_va_range_multicast(mm,
- start, end, pid, page_size, psize, also_pwc);
- }
- out:
- preempt_enable();
- }
- void radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start,
- unsigned long end, int psize)
- {
- return __radix__flush_tlb_range_psize(mm, start, end, psize, false);
- }
- void radix__flush_tlb_pwc_range_psize(struct mm_struct *mm, unsigned long start,
- unsigned long end, int psize)
- {
- __radix__flush_tlb_range_psize(mm, start, end, psize, true);
- }
- #ifdef CONFIG_TRANSPARENT_HUGEPAGE
- void radix__flush_tlb_collapsed_pmd(struct mm_struct *mm, unsigned long addr)
- {
- unsigned long pid, end;
- enum tlb_flush_type type;
- pid = mm->context.id;
- if (unlikely(pid == MMU_NO_CONTEXT))
- return;
- /* 4k page size, just blow the world */
- if (PAGE_SIZE == 0x1000) {
- radix__flush_all_mm(mm);
- return;
- }
- end = addr + HPAGE_PMD_SIZE;
- /* Otherwise first do the PWC, then iterate the pages. */
- preempt_disable();
- smp_mb(); /* see radix__flush_tlb_mm */
- type = flush_type_needed(mm, false);
- if (type == FLUSH_TYPE_LOCAL) {
- _tlbiel_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
- } else if (type == FLUSH_TYPE_GLOBAL) {
- if (!mmu_has_feature(MMU_FTR_GTSE)) {
- unsigned long tgt, type, pg_sizes;
- tgt = H_RPTI_TARGET_CMMU;
- type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC |
- H_RPTI_TYPE_PRT;
- pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize);
- if (atomic_read(&mm->context.copros) > 0)
- tgt |= H_RPTI_TARGET_NMMU;
- pseries_rpt_invalidate(pid, tgt, type, pg_sizes,
- addr, end);
- } else if (cputlb_use_tlbie())
- _tlbie_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
- else
- _tlbiel_va_range_multicast(mm,
- addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true);
- }
- preempt_enable();
- }
- #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
- void radix__flush_pmd_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
- {
- radix__flush_tlb_range_psize(vma->vm_mm, start, end, MMU_PAGE_2M);
- }
- EXPORT_SYMBOL(radix__flush_pmd_tlb_range);
- void radix__flush_tlb_all(void)
- {
- unsigned long rb,prs,r,rs;
- unsigned long ric = RIC_FLUSH_ALL;
- rb = 0x3 << PPC_BITLSHIFT(53); /* IS = 3 */
- prs = 0; /* partition scoped */
- r = 1; /* radix format */
- rs = 1 & ((1UL << 32) - 1); /* any LPID value to flush guest mappings */
- asm volatile("ptesync": : :"memory");
- /*
- * now flush guest entries by passing PRS = 1 and LPID != 0
- */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(1), "i"(ric), "r"(rs) : "memory");
- /*
- * now flush host entires by passing PRS = 0 and LPID == 0
- */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(0) : "memory");
- asm volatile("eieio; tlbsync; ptesync": : :"memory");
- }
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- static __always_inline void __tlbie_pid_lpid(unsigned long pid,
- unsigned long lpid,
- unsigned long ric)
- {
- unsigned long rb, rs, prs, r;
- rb = PPC_BIT(53); /* IS = 1 */
- rs = (pid << PPC_BITLSHIFT(31)) | (lpid & ~(PPC_BITMASK(0, 31)));
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(0, 0, rb, rs, ric, prs, r);
- }
- static __always_inline void __tlbie_va_lpid(unsigned long va, unsigned long pid,
- unsigned long lpid,
- unsigned long ap, unsigned long ric)
- {
- unsigned long rb, rs, prs, r;
- rb = va & ~(PPC_BITMASK(52, 63));
- rb |= ap << PPC_BITLSHIFT(58);
- rs = (pid << PPC_BITLSHIFT(31)) | (lpid & ~(PPC_BITMASK(0, 31)));
- prs = 1; /* process scoped */
- r = 1; /* radix format */
- asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
- : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
- trace_tlbie(0, 0, rb, rs, ric, prs, r);
- }
- static inline void fixup_tlbie_pid_lpid(unsigned long pid, unsigned long lpid)
- {
- /*
- * We can use any address for the invalidation, pick one which is
- * probably unused as an optimisation.
- */
- unsigned long va = ((1UL << 52) - 1);
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync" : : : "memory");
- __tlbie_pid_lpid(0, lpid, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync" : : : "memory");
- __tlbie_va_lpid(va, pid, lpid, mmu_get_ap(MMU_PAGE_64K),
- RIC_FLUSH_TLB);
- }
- }
- static inline void _tlbie_pid_lpid(unsigned long pid, unsigned long lpid,
- unsigned long ric)
- {
- asm volatile("ptesync" : : : "memory");
- /*
- * Workaround the fact that the "ric" argument to __tlbie_pid
- * must be a compile-time contraint to match the "i" constraint
- * in the asm statement.
- */
- switch (ric) {
- case RIC_FLUSH_TLB:
- __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB);
- fixup_tlbie_pid_lpid(pid, lpid);
- break;
- case RIC_FLUSH_PWC:
- __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC);
- break;
- case RIC_FLUSH_ALL:
- default:
- __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_ALL);
- fixup_tlbie_pid_lpid(pid, lpid);
- }
- asm volatile("eieio; tlbsync; ptesync" : : : "memory");
- }
- static inline void fixup_tlbie_va_range_lpid(unsigned long va,
- unsigned long pid,
- unsigned long lpid,
- unsigned long ap)
- {
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
- asm volatile("ptesync" : : : "memory");
- __tlbie_pid_lpid(0, lpid, RIC_FLUSH_TLB);
- }
- if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
- asm volatile("ptesync" : : : "memory");
- __tlbie_va_lpid(va, pid, lpid, ap, RIC_FLUSH_TLB);
- }
- }
- static inline void __tlbie_va_range_lpid(unsigned long start, unsigned long end,
- unsigned long pid, unsigned long lpid,
- unsigned long page_size,
- unsigned long psize)
- {
- unsigned long addr;
- unsigned long ap = mmu_get_ap(psize);
- for (addr = start; addr < end; addr += page_size)
- __tlbie_va_lpid(addr, pid, lpid, ap, RIC_FLUSH_TLB);
- fixup_tlbie_va_range_lpid(addr - page_size, pid, lpid, ap);
- }
- static inline void _tlbie_va_range_lpid(unsigned long start, unsigned long end,
- unsigned long pid, unsigned long lpid,
- unsigned long page_size,
- unsigned long psize, bool also_pwc)
- {
- asm volatile("ptesync" : : : "memory");
- if (also_pwc)
- __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC);
- __tlbie_va_range_lpid(start, end, pid, lpid, page_size, psize);
- asm volatile("eieio; tlbsync; ptesync" : : : "memory");
- }
- /*
- * Performs process-scoped invalidations for a given LPID
- * as part of H_RPT_INVALIDATE hcall.
- */
- void do_h_rpt_invalidate_prt(unsigned long pid, unsigned long lpid,
- unsigned long type, unsigned long pg_sizes,
- unsigned long start, unsigned long end)
- {
- unsigned long psize, nr_pages;
- struct mmu_psize_def *def;
- bool flush_pid;
- /*
- * A H_RPTI_TYPE_ALL request implies RIC=3, hence
- * do a single IS=1 based flush.
- */
- if ((type & H_RPTI_TYPE_ALL) == H_RPTI_TYPE_ALL) {
- _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_ALL);
- return;
- }
- if (type & H_RPTI_TYPE_PWC)
- _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC);
- /* Full PID flush */
- if (start == 0 && end == -1)
- return _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB);
- /* Do range invalidation for all the valid page sizes */
- for (psize = 0; psize < MMU_PAGE_COUNT; psize++) {
- def = &mmu_psize_defs[psize];
- if (!(pg_sizes & def->h_rpt_pgsize))
- continue;
- nr_pages = (end - start) >> def->shift;
- flush_pid = nr_pages > tlb_single_page_flush_ceiling;
- /*
- * If the number of pages spanning the range is above
- * the ceiling, convert the request into a full PID flush.
- * And since PID flush takes out all the page sizes, there
- * is no need to consider remaining page sizes.
- */
- if (flush_pid) {
- _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB);
- return;
- }
- _tlbie_va_range_lpid(start, end, pid, lpid,
- (1UL << def->shift), psize, false);
- }
- }
- EXPORT_SYMBOL_GPL(do_h_rpt_invalidate_prt);
- #endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
- static int __init create_tlb_single_page_flush_ceiling(void)
- {
- debugfs_create_u32("tlb_single_page_flush_ceiling", 0600,
- arch_debugfs_dir, &tlb_single_page_flush_ceiling);
- debugfs_create_u32("tlb_local_single_page_flush_ceiling", 0600,
- arch_debugfs_dir, &tlb_local_single_page_flush_ceiling);
- return 0;
- }
- late_initcall(create_tlb_single_page_flush_ceiling);
|