tlb-radix.c 11.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * TLB flush routines for radix kernels.
 *
 * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <linux/memblock.h>
15
#include <asm/ppc-opcode.h>
16 17 18

#include <asm/tlb.h>
#include <asm/tlbflush.h>
19
#include <asm/trace.h>
20 21


22 23 24 25 26 27
#define RIC_FLUSH_TLB 0
#define RIC_FLUSH_PWC 1
#define RIC_FLUSH_ALL 2

static inline void __tlbiel_pid(unsigned long pid, int set,
				unsigned long ric)
28
{
29
	unsigned long rb,rs,prs,r;
30 31 32 33 34 35 36

	rb = PPC_BIT(53); /* IS = 1 */
	rb |= set << PPC_BITLSHIFT(51);
	rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

37
	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
38
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
39
	trace_tlbie(0, 1, rb, rs, ric, prs, r);
40 41 42 43 44
}

/*
 * We use 128 set in radix mode and 256 set in hpt mode.
 */
45
static inline void _tlbiel_pid(unsigned long pid, unsigned long ric)
46 47 48
{
	int set;

49
	asm volatile("ptesync": : :"memory");
50 51 52 53 54 55 56

	/*
	 * 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);

57 58 59 60 61
	/* For PWC, only one flush is needed */
	if (ric == RIC_FLUSH_PWC) {
		asm volatile("ptesync": : :"memory");
		return;
	}
62

63
	/* For the remaining sets, just flush the TLB */
64
	for (set = 1; set < POWER9_TLB_SETS_RADIX ; set++)
65
		__tlbiel_pid(pid, set, RIC_FLUSH_TLB);
66

67
	asm volatile("ptesync": : :"memory");
68
	asm volatile(PPC_INVALIDATE_ERAT "; isync" : : :"memory");
69 70
}

71
static inline void _tlbie_pid(unsigned long pid, unsigned long ric)
72
{
73
	unsigned long rb,rs,prs,r;
74 75 76 77 78 79 80

	rb = PPC_BIT(53); /* IS = 1 */
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
81
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
82 83
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
84
	trace_tlbie(0, 0, rb, rs, ric, prs, r);
85 86 87
}

static inline void _tlbiel_va(unsigned long va, unsigned long pid,
88
			      unsigned long ap, unsigned long ric)
89
{
90
	unsigned long rb,rs,prs,r;
91 92 93 94 95 96 97 98

	rb = va & ~(PPC_BITMASK(52, 63));
	rb |= ap << PPC_BITLSHIFT(58);
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
99
	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
100 101
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("ptesync": : :"memory");
102
	trace_tlbie(0, 1, rb, rs, ric, prs, r);
103 104 105
}

static inline void _tlbie_va(unsigned long va, unsigned long pid,
106
			     unsigned long ap, unsigned long ric)
107
{
108
	unsigned long rb,rs,prs,r;
109 110 111 112 113 114 115 116

	rb = va & ~(PPC_BITMASK(52, 63));
	rb |= ap << PPC_BITLSHIFT(58);
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
117
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
118 119
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
120
	trace_tlbie(0, 0, rb, rs, ric, prs, r);
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
}

/*
 * 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)
{
136
	unsigned long pid;
137 138 139 140

	preempt_disable();
	pid = mm->context.id;
	if (pid != MMU_NO_CONTEXT)
141
		_tlbiel_pid(pid, RIC_FLUSH_TLB);
142 143 144 145
	preempt_enable();
}
EXPORT_SYMBOL(radix__local_flush_tlb_mm);

146 147
#ifndef CONFIG_SMP
static void radix__local_flush_all_mm(struct mm_struct *mm)
148 149 150 151 152 153
{
	unsigned long pid;

	preempt_disable();
	pid = mm->context.id;
	if (pid != MMU_NO_CONTEXT)
154
		_tlbiel_pid(pid, RIC_FLUSH_ALL);
155 156
	preempt_enable();
}
157
#endif /* CONFIG_SMP */
158

159
void radix__local_flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
160
				       int psize)
161
{
162
	unsigned long pid;
163
	unsigned long ap = mmu_get_ap(psize);
164 165 166 167

	preempt_disable();
	pid = mm ? mm->context.id : 0;
	if (pid != MMU_NO_CONTEXT)
168
		_tlbiel_va(vmaddr, pid, ap, RIC_FLUSH_TLB);
169 170 171 172 173
	preempt_enable();
}

void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
174 175 176 177 178
#ifdef CONFIG_HUGETLB_PAGE
	/* need the return fix for nohash.c */
	if (vma && is_vm_hugetlb_page(vma))
		return __local_flush_hugetlb_page(vma, vmaddr);
#endif
179
	radix__local_flush_tlb_page_psize(vma ? vma->vm_mm : NULL, vmaddr,
180
					  mmu_virtual_psize);
181 182 183 184 185 186
}
EXPORT_SYMBOL(radix__local_flush_tlb_page);

#ifdef CONFIG_SMP
void radix__flush_tlb_mm(struct mm_struct *mm)
{
187
	unsigned long pid;
188 189 190 191 192 193

	preempt_disable();
	pid = mm->context.id;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto no_context;

194
	if (!mm_is_thread_local(mm))
195
		_tlbie_pid(pid, RIC_FLUSH_TLB);
196
	else
197
		_tlbiel_pid(pid, RIC_FLUSH_TLB);
198 199 200 201 202
no_context:
	preempt_enable();
}
EXPORT_SYMBOL(radix__flush_tlb_mm);

203
static void radix__flush_all_mm(struct mm_struct *mm)
204 205 206 207 208 209 210 211
{
	unsigned long pid;

	preempt_disable();
	pid = mm->context.id;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto no_context;

212
	if (!mm_is_thread_local(mm))
213
		_tlbie_pid(pid, RIC_FLUSH_ALL);
214
	else
215
		_tlbiel_pid(pid, RIC_FLUSH_ALL);
216 217 218
no_context:
	preempt_enable();
}
219 220 221 222 223

void radix__flush_tlb_pwc(struct mmu_gather *tlb, unsigned long addr)
{
	tlb->need_flush_all = 1;
}
224 225
EXPORT_SYMBOL(radix__flush_tlb_pwc);

226
void radix__flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
227
				 int psize)
228
{
229
	unsigned long pid;
230
	unsigned long ap = mmu_get_ap(psize);
231 232 233 234 235

	preempt_disable();
	pid = mm ? mm->context.id : 0;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto bail;
236
	if (!mm_is_thread_local(mm))
237
		_tlbie_va(vmaddr, pid, ap, RIC_FLUSH_TLB);
238
	else
239
		_tlbiel_va(vmaddr, pid, ap, RIC_FLUSH_TLB);
240 241 242 243 244 245
bail:
	preempt_enable();
}

void radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
246 247 248 249
#ifdef CONFIG_HUGETLB_PAGE
	if (vma && is_vm_hugetlb_page(vma))
		return flush_hugetlb_page(vma, vmaddr);
#endif
250
	radix__flush_tlb_page_psize(vma ? vma->vm_mm : NULL, vmaddr,
251
				    mmu_virtual_psize);
252 253 254
}
EXPORT_SYMBOL(radix__flush_tlb_page);

255 256
#else /* CONFIG_SMP */
#define radix__flush_all_mm radix__local_flush_all_mm
257 258 259 260
#endif /* CONFIG_SMP */

void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
261
	_tlbie_pid(0, RIC_FLUSH_ALL);
262 263 264 265 266 267 268 269 270 271 272 273
}
EXPORT_SYMBOL(radix__flush_tlb_kernel_range);

/*
 * Currently, for range flushing, we just do a full mm flush. Because
 * we use this in code path where we don' track the page size.
 */
void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
		     unsigned long end)

{
	struct mm_struct *mm = vma->vm_mm;
274 275 276 277 278 279

	/*
	 * This is currently used when collapsing THPs so we need to
	 * flush the PWC. We should fix this.
	 */
	radix__flush_all_mm(mm);
280 281 282
}
EXPORT_SYMBOL(radix__flush_tlb_range);

283 284 285 286 287 288 289 290 291 292 293 294 295 296
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;
}
297 298 299

void radix__tlb_flush(struct mmu_gather *tlb)
{
300
	int psize = 0;
301
	struct mm_struct *mm = tlb->mm;
302 303 304 305 306 307 308 309
	int page_size = tlb->page_size;

	psize = radix_get_mmu_psize(page_size);
	/*
	 * if page size is not something we understand, do a full mm flush
	 */
	if (psize != -1 && !tlb->fullmm && !tlb->need_flush_all)
		radix__flush_tlb_range_psize(mm, tlb->start, tlb->end, psize);
310 311 312 313
	else if (tlb->need_flush_all) {
		tlb->need_flush_all = 0;
		radix__flush_all_mm(mm);
	} else
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
		radix__flush_tlb_mm(mm);
}

#define TLB_FLUSH_ALL -1UL
/*
 * Number of pages above which we will do a bcast tlbie. Just a
 * number at this point copied from x86
 */
static unsigned long tlb_single_page_flush_ceiling __read_mostly = 33;

void radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start,
				  unsigned long end, int psize)
{
	unsigned long pid;
	unsigned long addr;
329
	int local = mm_is_thread_local(mm);
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
	unsigned long ap = mmu_get_ap(psize);
	unsigned long page_size = 1UL << mmu_psize_defs[psize].shift;


	preempt_disable();
	pid = mm ? mm->context.id : 0;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto err_out;

	if (end == TLB_FLUSH_ALL ||
	    (end - start) > tlb_single_page_flush_ceiling * page_size) {
		if (local)
			_tlbiel_pid(pid, RIC_FLUSH_TLB);
		else
			_tlbie_pid(pid, RIC_FLUSH_TLB);
		goto err_out;
	}
	for (addr = start; addr < end; addr += page_size) {

		if (local)
			_tlbiel_va(addr, pid, ap, RIC_FLUSH_TLB);
351
		else
352 353 354 355
			_tlbie_va(addr, pid, ap, RIC_FLUSH_TLB);
	}
err_out:
	preempt_enable();
356
}
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

void radix__flush_tlb_lpid_va(unsigned long lpid, unsigned long gpa,
			      unsigned long page_size)
{
	unsigned long rb,rs,prs,r;
	unsigned long ap;
	unsigned long ric = RIC_FLUSH_TLB;

	ap = mmu_get_ap(radix_get_mmu_psize(page_size));
	rb = gpa & ~(PPC_BITMASK(52, 63));
	rb |= ap << PPC_BITLSHIFT(58);
	rs = lpid & ((1UL << 32) - 1);
	prs = 0; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
376
	trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
}
EXPORT_SYMBOL(radix__flush_tlb_lpid_va);

void radix__flush_tlb_lpid(unsigned long lpid)
{
	unsigned long rb,rs,prs,r;
	unsigned long ric = RIC_FLUSH_ALL;

	rb = 0x2 << PPC_BITLSHIFT(53); /* IS = 2 */
	rs = lpid & ((1UL << 32) - 1);
	prs = 0; /* partition scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
394
	trace_tlbie(lpid, 0, rb, rs, ric, prs, r);
395 396
}
EXPORT_SYMBOL(radix__flush_tlb_lpid);
397 398 399 400 401 402 403

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);
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420

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;   /* raidx 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");
421
	trace_tlbie(0, 0, rb, rs, ric, prs, r);
422 423 424 425 426 427
	/*
	 * 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");
428
	trace_tlbie(0, 0, rb, 0, ric, prs, r);
429
}
430 431 432 433 434 435 436 437 438 439 440 441 442

void radix__flush_tlb_pte_p9_dd1(unsigned long old_pte, struct mm_struct *mm,
				 unsigned long address)
{
	/*
	 * We track page size in pte only for DD1, So we can
	 * call this only on DD1.
	 */
	if (!cpu_has_feature(CPU_FTR_POWER9_DD1)) {
		VM_WARN_ON(1);
		return;
	}

443
	if (old_pte & R_PAGE_LARGE)
444 445 446 447
		radix__flush_tlb_page_psize(mm, address, MMU_PAGE_2M);
	else
		radix__flush_tlb_page_psize(mm, address, mmu_virtual_psize);
}