diff options
Diffstat (limited to 'arch/powerpc/include/asm/mmu-hash64.h')
| -rw-r--r-- | arch/powerpc/include/asm/mmu-hash64.h | 128 | 
1 files changed, 66 insertions, 62 deletions
| diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h index 2fdb47a19ef..b59e06f507e 100644 --- a/arch/powerpc/include/asm/mmu-hash64.h +++ b/arch/powerpc/include/asm/mmu-hash64.h @@ -343,17 +343,16 @@ extern void slb_set_size(u16 size);  /*   * VSID allocation (256MB segment)   * - * We first generate a 38-bit "proto-VSID".  For kernel addresses this - * is equal to the ESID | 1 << 37, for user addresses it is: - *	(context << USER_ESID_BITS) | (esid & ((1U << USER_ESID_BITS) - 1) + * We first generate a 37-bit "proto-VSID". Proto-VSIDs are generated + * from mmu context id and effective segment id of the address.   * - * This splits the proto-VSID into the below range - *  0 - (2^(CONTEXT_BITS + USER_ESID_BITS) - 1) : User proto-VSID range - *  2^(CONTEXT_BITS + USER_ESID_BITS) - 2^(VSID_BITS) : Kernel proto-VSID range - * - * We also have CONTEXT_BITS + USER_ESID_BITS = VSID_BITS - 1 - * That is, we assign half of the space to user processes and half - * to the kernel. + * For user processes max context id is limited to ((1ul << 19) - 5) + * for kernel space, we use the top 4 context ids to map address as below + * NOTE: each context only support 64TB now. + * 0x7fffc -  [ 0xc000000000000000 - 0xc0003fffffffffff ] + * 0x7fffd -  [ 0xd000000000000000 - 0xd0003fffffffffff ] + * 0x7fffe -  [ 0xe000000000000000 - 0xe0003fffffffffff ] + * 0x7ffff -  [ 0xf000000000000000 - 0xf0003fffffffffff ]   *   * The proto-VSIDs are then scrambled into real VSIDs with the   * multiplicative hash: @@ -363,41 +362,49 @@ extern void slb_set_size(u16 size);   * VSID_MULTIPLIER is prime, so in particular it is   * co-prime to VSID_MODULUS, making this a 1:1 scrambling function.   * Because the modulus is 2^n-1 we can compute it efficiently without - * a divide or extra multiply (see below). - * - * This scheme has several advantages over older methods: - * - *	- We have VSIDs allocated for every kernel address - * (i.e. everything above 0xC000000000000000), except the very top - * segment, which simplifies several things. + * a divide or extra multiply (see below). The scramble function gives + * robust scattering in the hash table (at least based on some initial + * results).   * - *	- We allow for USER_ESID_BITS significant bits of ESID and - * CONTEXT_BITS  bits of context for user addresses. - *  i.e. 64T (46 bits) of address space for up to half a million contexts. + * We also consider VSID 0 special. We use VSID 0 for slb entries mapping + * bad address. This enables us to consolidate bad address handling in + * hash_page.   * - *	- The scramble function gives robust scattering in the hash - * table (at least based on some initial results).  The previous - * method was more susceptible to pathological cases giving excessive - * hash collisions. + * We also need to avoid the last segment of the last context, because that + * would give a protovsid of 0x1fffffffff. That will result in a VSID 0 + * because of the modulo operation in vsid scramble. But the vmemmap + * (which is what uses region 0xf) will never be close to 64TB in size + * (it's 56 bytes per page of system memory).   */ +#define CONTEXT_BITS		19 +#define ESID_BITS		18 +#define ESID_BITS_1T		6 + +/* + * 256MB segment + * The proto-VSID space has 2^(CONTEX_BITS + ESID_BITS) - 1 segments + * available for user + kernel mapping. The top 4 contexts are used for + * kernel mapping. Each segment contains 2^28 bytes. Each + * context maps 2^46 bytes (64TB) so we can support 2^19-1 contexts + * (19 == 37 + 28 - 46). + */ +#define MAX_USER_CONTEXT	((ASM_CONST(1) << CONTEXT_BITS) - 5) +  /*   * This should be computed such that protovosid * vsid_mulitplier   * doesn't overflow 64 bits. It should also be co-prime to vsid_modulus   */  #define VSID_MULTIPLIER_256M	ASM_CONST(12538073)	/* 24-bit prime */ -#define VSID_BITS_256M		38 +#define VSID_BITS_256M		(CONTEXT_BITS + ESID_BITS)  #define VSID_MODULUS_256M	((1UL<<VSID_BITS_256M)-1)  #define VSID_MULTIPLIER_1T	ASM_CONST(12538073)	/* 24-bit prime */ -#define VSID_BITS_1T		26 +#define VSID_BITS_1T		(CONTEXT_BITS + ESID_BITS_1T)  #define VSID_MODULUS_1T		((1UL<<VSID_BITS_1T)-1) -#define CONTEXT_BITS		19 -#define USER_ESID_BITS		18 -#define USER_ESID_BITS_1T	6 -#define USER_VSID_RANGE	(1UL << (USER_ESID_BITS + SID_SHIFT)) +#define USER_VSID_RANGE	(1UL << (ESID_BITS + SID_SHIFT))  /*   * This macro generates asm code to compute the VSID scramble @@ -421,7 +428,8 @@ extern void slb_set_size(u16 size);  	srdi	rx,rt,VSID_BITS_##size;					\  	clrldi	rt,rt,(64-VSID_BITS_##size);				\  	add	rt,rt,rx;		/* add high and low bits */	\ -	/* Now, r3 == VSID (mod 2^36-1), and lies between 0 and		\ +	/* NOTE: explanation based on VSID_BITS_##size = 36		\ +	 * Now, r3 == VSID (mod 2^36-1), and lies between 0 and		\  	 * 2^36-1+2^28-1.  That in particular means that if r3 >=	\  	 * 2^36-1, then r3+1 has the 2^36 bit set.  So, if r3+1 has	\  	 * the bit clear, r3 already has the answer we want, if it	\ @@ -513,34 +521,6 @@ typedef struct {  	})  #endif /* 1 */ -/* - * This is only valid for addresses >= PAGE_OFFSET - * The proto-VSID space is divided into two class - * User:   0 to 2^(CONTEXT_BITS + USER_ESID_BITS) -1 - * kernel: 2^(CONTEXT_BITS + USER_ESID_BITS) to 2^(VSID_BITS) - 1 - * - * With KERNEL_START at 0xc000000000000000, the proto vsid for - * the kernel ends up with 0xc00000000 (36 bits). With 64TB - * support we need to have kernel proto-VSID in the - * [2^37 to 2^38 - 1] range due to the increased USER_ESID_BITS. - */ -static inline unsigned long get_kernel_vsid(unsigned long ea, int ssize) -{ -	unsigned long proto_vsid; -	/* -	 * We need to make sure proto_vsid for the kernel is -	 * >= 2^(CONTEXT_BITS + USER_ESID_BITS[_1T]) -	 */ -	if (ssize == MMU_SEGSIZE_256M) { -		proto_vsid = ea >> SID_SHIFT; -		proto_vsid |= (1UL << (CONTEXT_BITS + USER_ESID_BITS)); -		return vsid_scramble(proto_vsid, 256M); -	} -	proto_vsid = ea >> SID_SHIFT_1T; -	proto_vsid |= (1UL << (CONTEXT_BITS + USER_ESID_BITS_1T)); -	return vsid_scramble(proto_vsid, 1T); -} -  /* Returns the segment size indicator for a user address */  static inline int user_segment_size(unsigned long addr)  { @@ -550,17 +530,41 @@ static inline int user_segment_size(unsigned long addr)  	return MMU_SEGSIZE_256M;  } -/* This is only valid for user addresses (which are below 2^44) */  static inline unsigned long get_vsid(unsigned long context, unsigned long ea,  				     int ssize)  { +	/* +	 * Bad address. We return VSID 0 for that +	 */ +	if ((ea & ~REGION_MASK) >= PGTABLE_RANGE) +		return 0; +  	if (ssize == MMU_SEGSIZE_256M) -		return vsid_scramble((context << USER_ESID_BITS) +		return vsid_scramble((context << ESID_BITS)  				     | (ea >> SID_SHIFT), 256M); -	return vsid_scramble((context << USER_ESID_BITS_1T) +	return vsid_scramble((context << ESID_BITS_1T)  			     | (ea >> SID_SHIFT_1T), 1T);  } +/* + * This is only valid for addresses >= PAGE_OFFSET + * + * For kernel space, we use the top 4 context ids to map address as below + * 0x7fffc -  [ 0xc000000000000000 - 0xc0003fffffffffff ] + * 0x7fffd -  [ 0xd000000000000000 - 0xd0003fffffffffff ] + * 0x7fffe -  [ 0xe000000000000000 - 0xe0003fffffffffff ] + * 0x7ffff -  [ 0xf000000000000000 - 0xf0003fffffffffff ] + */ +static inline unsigned long get_kernel_vsid(unsigned long ea, int ssize) +{ +	unsigned long context; + +	/* +	 * kernel take the top 4 context from the available range +	 */ +	context = (MAX_USER_CONTEXT) + ((ea >> 60) - 0xc) + 1; +	return get_vsid(context, ea, ssize); +}  #endif /* __ASSEMBLY__ */  #endif /* _ASM_POWERPC_MMU_HASH64_H_ */ |