Skip to content

Commit

Permalink
[POWERPC] Fix switch_slb handling of 1T ESID values
Browse files Browse the repository at this point in the history
Now that we have 1TB segment size support, we need to be using the
GET_ESID_1T macro when comparing ESID values for pc, stack, and
unmapped_base within switch_slb().   A new helper function called
esids_match() contains the logic for deciding when to call GET_ESID
and GET_ESID_1T.

This fixes a duplicate-slb-entry inspired machine-check exception I
was seeing when trying to run java on a power6 partition.

Tested on power6 and power5.

Signed-off-by: Will Schmidt <will_schmidt@vnet.ibm.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
will schmidt authored and Paul Mackerras committed Nov 8, 2007
1 parent e7bda18 commit 465ccab
Showing 1 changed file with 31 additions and 3 deletions.
34 changes: 31 additions & 3 deletions arch/powerpc/mm/slb.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,35 @@ void slb_vmalloc_update(void)
slb_flush_and_rebolt();
}

/* Helper function to compare esids. There are four cases to handle.
* 1. The system is not 1T segment size capable. Use the GET_ESID compare.
* 2. The system is 1T capable, both addresses are < 1T, use the GET_ESID compare.
* 3. The system is 1T capable, only one of the two addresses is > 1T. This is not a match.
* 4. The system is 1T capable, both addresses are > 1T, use the GET_ESID_1T macro to compare.
*/
static inline int esids_match(unsigned long addr1, unsigned long addr2)
{
int esid_1t_count;

/* System is not 1T segment size capable. */
if (!cpu_has_feature(CPU_FTR_1T_SEGMENT))
return (GET_ESID(addr1) == GET_ESID(addr2));

esid_1t_count = (((addr1 >> SID_SHIFT_1T) != 0) +
((addr2 >> SID_SHIFT_1T) != 0));

/* both addresses are < 1T */
if (esid_1t_count == 0)
return (GET_ESID(addr1) == GET_ESID(addr2));

/* One address < 1T, the other > 1T. Not a match */
if (esid_1t_count == 1)
return 0;

/* Both addresses are > 1T. */
return (GET_ESID_1T(addr1) == GET_ESID_1T(addr2));
}

/* Flush all user entries from the segment table of the current processor. */
void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
{
Expand Down Expand Up @@ -194,15 +223,14 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
return;
slb_allocate(pc);

if (GET_ESID(pc) == GET_ESID(stack))
if (esids_match(pc,stack))
return;

if (is_kernel_addr(stack))
return;
slb_allocate(stack);

if ((GET_ESID(pc) == GET_ESID(unmapped_base))
|| (GET_ESID(stack) == GET_ESID(unmapped_base)))
if (esids_match(pc,unmapped_base) || esids_match(stack,unmapped_base))
return;

if (is_kernel_addr(unmapped_base))
Expand Down

0 comments on commit 465ccab

Please sign in to comment.