hash_native.c 10.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * native hashtable management.
 *
 * SMP scalability work:
 *    Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
 * 
 * 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/spinlock.h>
#include <linux/bitops.h>
#include <linux/threads.h>
#include <linux/smp.h>

#include <asm/abs_addr.h>
#include <asm/machdep.h>
#include <asm/mmu.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
#include <asm/cputable.h>

#define HPTE_LOCK_BIT 3

static DEFINE_SPINLOCK(native_tlbie_lock);

30
static inline void native_lock_hpte(hpte_t *hptep)
L
Linus Torvalds 已提交
31
{
32
	unsigned long *word = &hptep->v;
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40 41

	while (1) {
		if (!test_and_set_bit(HPTE_LOCK_BIT, word))
			break;
		while(test_bit(HPTE_LOCK_BIT, word))
			cpu_relax();
	}
}

42
static inline void native_unlock_hpte(hpte_t *hptep)
L
Linus Torvalds 已提交
43
{
44
	unsigned long *word = &hptep->v;
L
Linus Torvalds 已提交
45 46 47 48 49 50

	asm volatile("lwsync":::"memory");
	clear_bit(HPTE_LOCK_BIT, word);
}

long native_hpte_insert(unsigned long hpte_group, unsigned long va,
51 52
			unsigned long prpn, unsigned long vflags,
			unsigned long rflags)
L
Linus Torvalds 已提交
53 54
{
	unsigned long arpn = physRpn_to_absRpn(prpn);
55 56
	hpte_t *hptep = htab_address + hpte_group;
	unsigned long hpte_v, hpte_r;
L
Linus Torvalds 已提交
57 58 59
	int i;

	for (i = 0; i < HPTES_PER_GROUP; i++) {
60
		if (! (hptep->v & HPTE_V_VALID)) {
L
Linus Torvalds 已提交
61 62
			/* retry with lock held */
			native_lock_hpte(hptep);
63
			if (! (hptep->v & HPTE_V_VALID))
L
Linus Torvalds 已提交
64 65 66 67 68 69 70 71 72 73
				break;
			native_unlock_hpte(hptep);
		}

		hptep++;
	}

	if (i == HPTES_PER_GROUP)
		return -1;

74 75 76 77
	hpte_v = (va >> 23) << HPTE_V_AVPN_SHIFT | vflags | HPTE_V_VALID;
	if (vflags & HPTE_V_LARGE)
		va &= ~(1UL << HPTE_V_AVPN_SHIFT);
	hpte_r = (arpn << HPTE_R_RPN_SHIFT) | rflags;
L
Linus Torvalds 已提交
78

79
	hptep->r = hpte_r;
L
Linus Torvalds 已提交
80 81 82 83 84 85
	/* Guarantee the second dword is visible before the valid bit */
	__asm__ __volatile__ ("eieio" : : : "memory");
	/*
	 * Now set the first dword including the valid bit
	 * NOTE: this also unlocks the hpte
	 */
86
	hptep->v = hpte_v;
L
Linus Torvalds 已提交
87 88 89

	__asm__ __volatile__ ("ptesync" : : : "memory");

90
	return i | (!!(vflags & HPTE_V_SECONDARY) << 3);
L
Linus Torvalds 已提交
91 92 93 94
}

static long native_hpte_remove(unsigned long hpte_group)
{
95
	hpte_t *hptep;
L
Linus Torvalds 已提交
96 97
	int i;
	int slot_offset;
98
	unsigned long hpte_v;
L
Linus Torvalds 已提交
99 100 101 102 103 104

	/* pick a random entry to start at */
	slot_offset = mftb() & 0x7;

	for (i = 0; i < HPTES_PER_GROUP; i++) {
		hptep = htab_address + hpte_group + slot_offset;
105
		hpte_v = hptep->v;
L
Linus Torvalds 已提交
106

107
		if ((hpte_v & HPTE_V_VALID) && !(hpte_v & HPTE_V_BOLTED)) {
L
Linus Torvalds 已提交
108 109
			/* retry with lock held */
			native_lock_hpte(hptep);
110 111 112
			hpte_v = hptep->v;
			if ((hpte_v & HPTE_V_VALID)
			    && !(hpte_v & HPTE_V_BOLTED))
L
Linus Torvalds 已提交
113 114 115 116 117 118 119 120 121 122 123 124
				break;
			native_unlock_hpte(hptep);
		}

		slot_offset++;
		slot_offset &= 0x7;
	}

	if (i == HPTES_PER_GROUP)
		return -1;

	/* Invalidate the hpte. NOTE: this also unlocks it */
125
	hptep->v = 0;
L
Linus Torvalds 已提交
126 127 128 129

	return i;
}

130
static inline void set_pp_bit(unsigned long pp, hpte_t *addr)
L
Linus Torvalds 已提交
131 132
{
	unsigned long old;
133
	unsigned long *p = &addr->r;
L
Linus Torvalds 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

	__asm__ __volatile__(
	"1:	ldarx	%0,0,%3\n\
		rldimi	%0,%2,0,61\n\
		stdcx.	%0,0,%3\n\
		bne	1b"
	: "=&r" (old), "=m" (*p)
	: "r" (pp), "r" (p), "m" (*p)
	: "cc");
}

/*
 * Only works on small pages. Yes its ugly to have to check each slot in
 * the group but we only use this during bootup.
 */
static long native_hpte_find(unsigned long vpn)
{
151
	hpte_t *hptep;
L
Linus Torvalds 已提交
152 153 154
	unsigned long hash;
	unsigned long i, j;
	long slot;
155
	unsigned long hpte_v;
L
Linus Torvalds 已提交
156 157 158 159 160 161 162

	hash = hpt_hash(vpn, 0);

	for (j = 0; j < 2; j++) {
		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
		for (i = 0; i < HPTES_PER_GROUP; i++) {
			hptep = htab_address + slot;
163
			hpte_v = hptep->v;
L
Linus Torvalds 已提交
164

165 166 167
			if ((HPTE_V_AVPN_VAL(hpte_v) == (vpn >> 11))
			    && (hpte_v & HPTE_V_VALID)
			    && ( !!(hpte_v & HPTE_V_SECONDARY) == j)) {
L
Linus Torvalds 已提交
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
				/* HPTE matches */
				if (j)
					slot = -slot;
				return slot;
			}
			++slot;
		}
		hash = ~hash;
	}

	return -1;
}

static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
				 unsigned long va, int large, int local)
{
184 185
	hpte_t *hptep = htab_address + slot;
	unsigned long hpte_v;
L
Linus Torvalds 已提交
186 187 188 189
	unsigned long avpn = va >> 23;
	int ret = 0;

	if (large)
190
		avpn &= ~1;
L
Linus Torvalds 已提交
191 192 193

	native_lock_hpte(hptep);

194
	hpte_v = hptep->v;
L
Linus Torvalds 已提交
195 196

	/* Even if we miss, we need to invalidate the TLB */
197 198
	if ((HPTE_V_AVPN_VAL(hpte_v) != avpn)
	    || !(hpte_v & HPTE_V_VALID)) {
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
		native_unlock_hpte(hptep);
		ret = -1;
	} else {
		set_pp_bit(newpp, hptep);
		native_unlock_hpte(hptep);
	}

	/* Ensure it is out of the tlb too */
	if (cpu_has_feature(CPU_FTR_TLBIEL) && !large && local) {
		tlbiel(va);
	} else {
		int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE);

		if (lock_tlbie)
			spin_lock(&native_tlbie_lock);
		tlbie(va, large);
		if (lock_tlbie)
			spin_unlock(&native_tlbie_lock);
	}

	return ret;
}

/*
 * Update the page protection bits. Intended to be used to create
 * guard pages for kernel data structures on pages which are bolted
 * in the HPT. Assumes pages being operated on will not be stolen.
 * Does not work on large pages.
 *
 * No need to lock here because we should be the only user.
 */
static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea)
{
	unsigned long vsid, va, vpn, flags = 0;
	long slot;
234
	hpte_t *hptep;
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE);

	vsid = get_kernel_vsid(ea);
	va = (vsid << 28) | (ea & 0x0fffffff);
	vpn = va >> PAGE_SHIFT;

	slot = native_hpte_find(vpn);
	if (slot == -1)
		panic("could not find page to bolt\n");
	hptep = htab_address + slot;

	set_pp_bit(newpp, hptep);

	/* Ensure it is out of the tlb too */
	if (lock_tlbie)
		spin_lock_irqsave(&native_tlbie_lock, flags);
	tlbie(va, 0);
	if (lock_tlbie)
		spin_unlock_irqrestore(&native_tlbie_lock, flags);
}

static void native_hpte_invalidate(unsigned long slot, unsigned long va,
				    int large, int local)
{
259 260
	hpte_t *hptep = htab_address + slot;
	unsigned long hpte_v;
L
Linus Torvalds 已提交
261 262 263 264 265
	unsigned long avpn = va >> 23;
	unsigned long flags;
	int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE);

	if (large)
266
		avpn &= ~1;
L
Linus Torvalds 已提交
267 268 269 270

	local_irq_save(flags);
	native_lock_hpte(hptep);

271
	hpte_v = hptep->v;
L
Linus Torvalds 已提交
272 273

	/* Even if we miss, we need to invalidate the TLB */
274 275
	if ((HPTE_V_AVPN_VAL(hpte_v) != avpn)
	    || !(hpte_v & HPTE_V_VALID)) {
L
Linus Torvalds 已提交
276 277 278
		native_unlock_hpte(hptep);
	} else {
		/* Invalidate the hpte. NOTE: this also unlocks it */
279
		hptep->v = 0;
L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
	}

	/* Invalidate the tlb */
	if (cpu_has_feature(CPU_FTR_TLBIEL) && !large && local) {
		tlbiel(va);
	} else {
		if (lock_tlbie)
			spin_lock(&native_tlbie_lock);
		tlbie(va, large);
		if (lock_tlbie)
			spin_unlock(&native_tlbie_lock);
	}
	local_irq_restore(flags);
}

295 296 297 298 299 300 301 302 303 304 305
/*
 * clear all mappings on kexec.  All cpus are in real mode (or they will
 * be when they isi), and we are the only one left.  We rely on our kernel
 * mapping being 0xC0's and the hardware ignoring those two real bits.
 *
 * TODO: add batching support when enabled.  remember, no dynamic memory here,
 * athough there is the control page available...
 */
static void native_hpte_clear(void)
{
	unsigned long slot, slots, flags;
306 307
	hpte_t *hptep = htab_address;
	unsigned long hpte_v;
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	unsigned long pteg_count;

	pteg_count = htab_hash_mask + 1;

	local_irq_save(flags);

	/* we take the tlbie lock and hold it.  Some hardware will
	 * deadlock if we try to tlbie from two processors at once.
	 */
	spin_lock(&native_tlbie_lock);

	slots = pteg_count * HPTES_PER_GROUP;

	for (slot = 0; slot < slots; slot++, hptep++) {
		/*
		 * we could lock the pte here, but we are the only cpu
		 * running,  right?  and for crash dump, we probably
		 * don't want to wait for a maybe bad cpu.
		 */
327
		hpte_v = hptep->v;
328

329 330 331
		if (hpte_v & HPTE_V_VALID) {
			hptep->v = 0;
			tlbie(slot2va(hpte_v, slot), hpte_v & HPTE_V_LARGE);
332 333 334 335 336 337 338
		}
	}

	spin_unlock(&native_tlbie_lock);
	local_irq_restore(flags);
}

L
Linus Torvalds 已提交
339 340 341 342 343
static void native_flush_hash_range(unsigned long context,
				    unsigned long number, int local)
{
	unsigned long vsid, vpn, va, hash, secondary, slot, flags, avpn;
	int i, j;
344 345
	hpte_t *hptep;
	unsigned long hpte_v;
L
Linus Torvalds 已提交
346 347 348 349 350 351 352 353 354
	struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);

	/* XXX fix for large ptes */
	unsigned long large = 0;

	local_irq_save(flags);

	j = 0;
	for (i = 0; i < number; i++) {
355
		if (batch->addr[i] < KERNELBASE)
L
Linus Torvalds 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
			vsid = get_vsid(context, batch->addr[i]);
		else
			vsid = get_kernel_vsid(batch->addr[i]);

		va = (vsid << 28) | (batch->addr[i] & 0x0fffffff);
		batch->vaddr[j] = va;
		if (large)
			vpn = va >> HPAGE_SHIFT;
		else
			vpn = va >> PAGE_SHIFT;
		hash = hpt_hash(vpn, large);
		secondary = (pte_val(batch->pte[i]) & _PAGE_SECONDARY) >> 15;
		if (secondary)
			hash = ~hash;
		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
		slot += (pte_val(batch->pte[i]) & _PAGE_GROUP_IX) >> 12;

		hptep = htab_address + slot;

		avpn = va >> 23;
		if (large)
			avpn &= ~0x1UL;

		native_lock_hpte(hptep);

381
		hpte_v = hptep->v;
L
Linus Torvalds 已提交
382 383

		/* Even if we miss, we need to invalidate the TLB */
384 385
		if ((HPTE_V_AVPN_VAL(hpte_v) != avpn)
		    || !(hpte_v & HPTE_V_VALID)) {
L
Linus Torvalds 已提交
386 387 388
			native_unlock_hpte(hptep);
		} else {
			/* Invalidate the hpte. NOTE: this also unlocks it */
389
			hptep->v = 0;
L
Linus Torvalds 已提交
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
		}

		j++;
	}

	if (cpu_has_feature(CPU_FTR_TLBIEL) && !large && local) {
		asm volatile("ptesync":::"memory");

		for (i = 0; i < j; i++)
			__tlbiel(batch->vaddr[i]);

		asm volatile("ptesync":::"memory");
	} else {
		int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE);

		if (lock_tlbie)
			spin_lock(&native_tlbie_lock);

		asm volatile("ptesync":::"memory");

		for (i = 0; i < j; i++)
			__tlbie(batch->vaddr[i], 0);

		asm volatile("eieio; tlbsync; ptesync":::"memory");

		if (lock_tlbie)
			spin_unlock(&native_tlbie_lock);
	}

	local_irq_restore(flags);
}

#ifdef CONFIG_PPC_PSERIES
/* Disable TLB batching on nighthawk */
static inline int tlb_batching_enabled(void)
{
	struct device_node *root = of_find_node_by_path("/");
	int enabled = 1;

	if (root) {
		const char *model = get_property(root, "model", NULL);
		if (model && !strcmp(model, "IBM,9076-N81"))
			enabled = 0;
		of_node_put(root);
	}

	return enabled;
}
#else
static inline int tlb_batching_enabled(void)
{
	return 1;
}
#endif

void hpte_init_native(void)
{
	ppc_md.hpte_invalidate	= native_hpte_invalidate;
	ppc_md.hpte_updatepp	= native_hpte_updatepp;
	ppc_md.hpte_updateboltedpp = native_hpte_updateboltedpp;
	ppc_md.hpte_insert	= native_hpte_insert;
451 452
	ppc_md.hpte_remove	= native_hpte_remove;
	ppc_md.hpte_clear_all	= native_hpte_clear;
L
Linus Torvalds 已提交
453 454 455 456
	if (tlb_batching_enabled())
		ppc_md.flush_hash_range = native_flush_hash_range;
	htab_finish_init();
}