blob: 70c08446db9d3839304ea307063ec34fe6b2d8a7 [file] [log] [blame]
/*********************************************************************
*
* (C) Copyright IBM Corp. 2010
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses>.
*
* Author: Chris Ward <tjcw@uk.ibm.com>
*
*
* Description: Blue Gene/P low-level driver for copy_tofrom_user thorough the
* parallel floating point unit
*
*
*
********************************************************************/
#define REQUIRES_DUMPMEM
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>
#include <linux/highmem.h>
#include <linux/mman.h>
#include <linux/syscalls.h>
#include <linux/pagemap.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/time.h>
#include <asm/bitops.h>
#include <asm/time.h>
#include "../bgp_network/bgp_net_traceflags.h"
#include <common/bgp_bitnumbers.h>
//#include "bgp_bic_diagnosis.h"
#include "../bgp_network/bgdiagnose.h"
#include "../bgp_network/450_tlb.h"
/* Can drop bits out of COMPILED_TRACEMASK if we want to selectively compile out trace */
#define COMPILED_TRACEMASK (0xffffffff)
/* #define COMPILED_TRACEMASK (k_t_error) */
#include <linux/KernelFxLog.h>
MODULE_DESCRIPTION("BG/P memory copy through parallel floating point registers");
MODULE_LICENSE("GPL");
#if defined(CONFIG_BLUEGENE_TORUS_TRACE)
int bgp_fpu_memcpy_tracemask = k_t_error ;
#define TRACEN(i,x...) KernelFxLog(bgp_fpu_memcpy_tracemask & (COMPILED_TRACEMASK & (i)),x)
#else
#define TRACEN(i,x...)
#endif
#include "bgp_memcpy.h"
#if defined(ADVENTUROUS_COPY_OPTIONS)
enum {
k_force_mask = 0 ,
k_enable_mask = 0 ,
k_inhibit_fpu_in_slih = 0
};
#else
enum {
k_force_mask = 0 ,
k_enable_mask = 1 ,
k_inhibit_fpu_in_slih = 0
};
#endif
enum {
k_page_shift = PAGE_SHIFT ,
k_page_size = 1 << k_page_shift ,
k_page_offset_mask = k_page_size-1 ,
k_fpu_alignment = 16 ,
k_fpu_align_mask = k_fpu_alignment - 1
} ;
static int source_alignment_statistics[k_fpu_alignment] ;
static int dest_alignment_statistics[k_fpu_alignment] ;
static int mutual_alignment_statistics[k_fpu_alignment] ;
struct ctl_table bgp_memcpy_table[] = {
{
.ctl_name = CTL_UNNUMBERED,
.procname = "use_dma",
.data = &bgp_memcpy_control.use_dma,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "verify_fpu",
.data = &bgp_memcpy_control.verify_fpu,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "verify_dma",
.data = &bgp_memcpy_control.verify_dma,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "use_fpu",
.data = &bgp_memcpy_control.use_fpu,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "dma_threshold",
.data = &bgp_memcpy_control.dma_threshold,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "fpu_threshold",
.data = &bgp_memcpy_control.fpu_threshold,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "faults_until_disable",
.data = &bgp_memcpy_control.faults_until_disable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "cycles_per_packet",
.data = &bgp_memcpy_control.cycles_per_packet,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "rate_observe_report_count",
.data = &bgp_memcpy_control.rate_observe_report_count,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "handle_pagecrossing",
.data = &bgp_memcpy_control.handle_pagecrossing,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "fpu_handle_pagecrossing_read",
.data = &bgp_memcpy_control.fpu_handle_pagecrossing_read,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "fpu_handle_pagecrossing_write",
.data = &bgp_memcpy_control.fpu_handle_pagecrossing_write,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "mask",
.data = &bgp_memcpy_control.mask,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "assist_active",
.data = &bgp_memcpy_control.assist_active,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "statistics",
.data = &bgp_dma_memcpy_statistics,
.maxlen = k_copy_statistics*sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "source_alignment_statistics",
.data = &source_alignment_statistics,
.maxlen = k_fpu_alignment*sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "dest_alignment_statistics",
.data = &dest_alignment_statistics,
.maxlen = k_fpu_alignment*sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
{
.ctl_name = CTL_UNNUMBERED,
.procname = "mutual_alignment_statistics",
.data = &mutual_alignment_statistics,
.maxlen = k_fpu_alignment*sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
#if defined(CONFIG_BLUEGENE_TORUS_TRACE)
{
.ctl_name = CTL_UNNUMBERED,
.procname = "tracemask",
.data = &bgp_fpu_memcpy_tracemask,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
} ,
#endif
{ 0 },
} ;
static struct ctl_path memcpy_ctl_path[] = {
{ .procname = "bgp", .ctl_name = 0, },
{ .procname = "copy", .ctl_name = 0, },
{ },
};
bgp_memcpy_control_t bgp_memcpy_control =
{
.use_dma = 0 ,
.use_fpu = 0 , // We suspect some kind of interaction with interrupts which makes it occasionally not work ...
.dma_threshold = 10000 ,
.fpu_threshold = 512 ,
.verify_dma = 0 ,
.verify_fpu = 0 ,
.cycles_per_packet = 20 ,
.rate_observe_report_count = 0xffffffff ,
.faults_until_disable = 1 ,
.handle_pagecrossing = 1 ,
.fpu_handle_pagecrossing_read = 0 ,
.fpu_handle_pagecrossing_write = 0 ,
.mask = 1 ,
.assist_active = 0
};
unsigned int bgp_dma_memcpy_statistics[k_copy_statistics] ;
static void cause_fallback(void)
{
TRACEN(k_t_request,"Turning off DH memcpy") ;
bgp_memcpy_control.use_fpu = 0 ;
dma_memcpy_statistic(k_copy_cause_fallback) ;
}
enum {
k_diag_not_mapped=0
/* k_diagnose=1 */
};
enum {
k_exploit_doublehummer = 1,
k_verify_doublehummer = 1,
k_fixup_faulty_memcpy=1,
k_premark=0 ,
k_map_write_check=0 ,
k_map_read_check=0 ,
k_disable_after_too_many_faults=1 ,
k_inhibit_crosspage_write = 1 , // Set this if you want to not handle writes which cross a user-space page boundary
k_inhibit_crosspage_read = 1 // Set this if you want to not handle reads which cross a user-space page boundary
};
static void report_faulty_memcpy(void * dest, const void * src, unsigned long size)
{
unsigned int * di = (unsigned int *) dest ;
const unsigned int * si = (const unsigned int *) src ;
unsigned char * dc = (unsigned char *) (dest) ;
const unsigned char * sc = (const unsigned char *) (src) ;
unsigned int x ;
unsigned int faultwordcount = 0 ;
if( k_disable_after_too_many_faults)
{
int faults_to_go=bgp_memcpy_control.faults_until_disable-1 ;
if( faults_to_go <= 0 )
{
cause_fallback() ;
}
else
{
bgp_memcpy_control.faults_until_disable=faults_to_go ;
}
}
dma_memcpy_statistic(k_copy_verify_miscompares) ;
TRACEN(k_t_error,"dest=%p src=%p size=0x%08lx",dest,src,size) ;
for(x=0;x<size/sizeof(unsigned int);x+=1)
{
if( di[x] != si[x] )
{
TRACEN(k_t_error,"(E) x=0x%08x di+x=%p si+x=%p di[x]=0x%08x si[x]=0x%08x",
x,di+x,si+x,di[x],si[x]) ;
if( k_fixup_faulty_memcpy) di[x]=si[x] ;
faultwordcount += 1 ;
}
}
if( dc[size-3] != sc[size-3])
{
TRACEN(k_t_error,"(E) x=0x%08lx dc+x=%p sc+x=%p dc[x]=0x%02x sc[x]=0x%02x",
size-3,dc+size-3,sc+size-3,dc[size-3],sc[size-3]) ;
if( k_fixup_faulty_memcpy) dc[size-3]=sc[size-3] ;
}
if( dc[size-2] != sc[size-2])
{
TRACEN(k_t_error,"(E) x=0x%08lx dc+x=%p sc+x=%p dc[x]=0x%02x sc[x]=0x%02x",
size-2,dc+size-2,sc+size-2,dc[size-2],sc[size-2]) ;
if( k_fixup_faulty_memcpy) dc[size-2]=sc[size-2] ;
}
if( dc[size-1] != sc[size-1])
{
TRACEN(k_t_error,"(E) x=0x%08lx dc+x=%p sc+x=%p dc[x]=0x%02x sc[x]=0x%02x",
size-1,dc+size-1,sc+size-1,dc[size-1],sc[size-1]) ;
if( k_fixup_faulty_memcpy) dc[size-1]=sc[size-1] ;
}
TRACEN(k_t_error,"%d/%ld words incorrectly copied",faultwordcount,size/sizeof(unsigned int)) ;
}
/* Check that a 'memcpy' was accurately done ... */
static void verify_memcpy(void * dest, const void * src, unsigned long size)
{
unsigned int * di = (unsigned int *) dest ;
const unsigned int * si = (const unsigned int *) src ;
unsigned char * dc = (unsigned char *) (dest) ;
const unsigned char * sc = (const unsigned char *) (src) ;
unsigned int q = di[0] ^ si[0] ;
unsigned int x ;
dma_memcpy_statistic(k_copy_verify_attempts) ;
TRACEN(k_t_fpucopy,"dest=%p src=%p size=0x%08lx di[0]=0x%08x si[0]=0x%08x",dest,src,size,di[0],si[0]) ;
for(x=1;x<size/sizeof(unsigned int);x+=1)
{
q |= di[x] ^ si[x] ;
}
q |= (dc[size-3] ^ sc[size-3]) |(dc[size-2] ^ sc[size-2]) |(dc[size-1] ^ sc[size-1]) ;
if(q) report_faulty_memcpy(dest,src,size) ;
}
typedef struct { unsigned char c[128] ; } miniblock ;
#define nl "\n"
/* Returns 0 for a good copy, 1 if an exception (unmapped storage) occurred */
static int doublehummer_copy_unroll(void *to, const void *from, int count)
{
int x1=0x10 ;
int x2=0x20 ;
int x3=0x30 ;
int x4=0x40 ;
int x5=0x50 ;
int x6=0x60 ;
int x7=0x70 ;
int x8=0x80 ;
int xa=0xa0 ;
int xc=0xc0 ;
int xe=0xe0 ;
int rc ;
asm volatile (
"mtctr %[count]" nl
"100: lfpdx 0,0,%[src]" nl
"101: lfpdx 2,%[index2],%[src]" nl
"102: lfpdx 4,%[index4],%[src]" nl
"103: lfpdx 6,%[index6],%[src]" nl
"104: lfpdx 1,%[index1],%[src]" nl
"105: lfpdx 3,%[index3],%[src]" nl
"106: lfpdx 5,%[index5],%[src]" nl
"107: lfpdx 7,%[index7],%[src]" nl
"108: stfpdx 0,0 ,%[dst]" nl
"109: lfpdx 0,%[index8],%[src]" nl
"110: stfpdx 2,%[index2],%[dst]" nl
"111: lfpdx 2,%[indexa],%[src]" nl
"112: stfpdx 4,%[index4],%[dst]" nl
"113: lfpdx 4,%[indexc],%[src]" nl
"114: stfpdx 6,%[index6],%[dst]" nl
"115: lfpdx 6,%[indexe],%[src]" nl
"bdz 1f" nl
"0:" nl
"addi %[src],%[src],128" nl
"116: stfpdx 1,%[index1],%[dst]" nl
"117: lfpdx 1,%[index1],%[src]" nl
"118: stfpdx 0,%[index8],%[dst]" nl
"119: lfpdx 0,%[index8],%[src]" nl
"120: stfpdx 3,%[index3],%[dst]" nl
"121: lfpdx 3,%[index3],%[src]" nl
"122: stfpdx 2,%[indexa],%[dst]" nl
"123: lfpdx 2,%[indexa],%[src]" nl
"124: stfpdx 5,%[index5],%[dst]" nl
"125: lfpdx 5,%[index5],%[src]" nl
"126: stfpdx 4,%[indexc],%[dst]" nl
"127: lfpdx 4,%[indexc],%[src]" nl
"128: stfpdx 7,%[index7],%[dst]" nl
"129: lfpdx 7,%[index7],%[src]" nl
"130: stfpdx 6,%[indexe],%[dst]" nl
"addi %[dst],%[dst],128" nl
"131: lfpdx 6,%[indexe],%[src]" nl
"bdnz 0b" nl
"1:" nl
"addi %[src],%[src],128" nl
"132: stfpdx 1,%[index1],%[dst]" nl
"133: lfpdx 1,%[index1],%[src]" nl
"134: stfpdx 0,%[index8],%[dst]" nl
"135: stfpdx 3,%[index3],%[dst]" nl
"136: lfpdx 3,%[index3],%[src]" nl
"137: stfpdx 2,%[indexa],%[dst]" nl
"138: stfpdx 5,%[index5],%[dst]" nl
"139: lfpdx 5,%[index5],%[src]" nl
"140: stfpdx 4,%[indexc],%[dst]" nl
"141: stfpdx 7,%[index7],%[dst]" nl
"142: lfpdx 7,%[index7],%[src]" nl
"143: stfpdx 6,%[indexe],%[dst]" nl
"addi %[dst],%[dst],128" nl
"144: stfpdx 1,%[index1],%[dst]" nl
"145: stfpdx 3,%[index3],%[dst]" nl
"146: stfpdx 5,%[index5],%[dst]" nl
"147: stfpdx 7,%[index7],%[dst]" nl
/* Following section needed to handle exceptions (user code passing addresses which SEGV) */
"li %[rc],0" nl
"b 3f" nl
"2:" nl
"li %[rc],1" nl
"3:" nl
".section __ex_table,\"a\"" nl
".align 2" nl
".long 100b,2b" nl
".long 101b,2b" nl
".long 102b,2b" nl
".long 103b,2b" nl
".long 104b,2b" nl
".long 105b,2b" nl
".long 106b,2b" nl
".long 107b,2b" nl
".long 108b,2b" nl
".long 109b,2b" nl
".long 110b,2b" nl
".long 111b,2b" nl
".long 112b,2b" nl
".long 113b,2b" nl
".long 114b,2b" nl
".long 115b,2b" nl
".long 116b,2b" nl
".long 117b,2b" nl
".long 118b,2b" nl
".long 119b,2b" nl
".long 120b,2b" nl
".long 121b,2b" nl
".long 122b,2b" nl
".long 123b,2b" nl
".long 124b,2b" nl
".long 125b,2b" nl
".long 126b,2b" nl
".long 127b,2b" nl
".long 128b,2b" nl
".long 129b,2b" nl
".long 130b,2b" nl
".long 131b,2b" nl
".long 132b,2b" nl
".long 133b,2b" nl
".long 134b,2b" nl
".long 135b,2b" nl
".long 136b,2b" nl
".long 137b,2b" nl
".long 138b,2b" nl
".long 139b,2b" nl
".long 140b,2b" nl
".long 141b,2b" nl
".long 142b,2b" nl
".long 143b,2b" nl
".long 144b,2b" nl
".long 145b,2b" nl
".long 146b,2b" nl
".long 147b,2b" nl
".text" nl
: /* Outputs */
[rc] "=b" (rc)
: /* Inputs */
[dst] "b" (to),
[src] "b" (from),
[count] "r" (count),
[index1] "b" (x1),
[index2] "b" (x2),
[index3] "b" (x3),
[index4] "b" (x4),
[index5] "b" (x5),
[index6] "b" (x6),
[index7] "b" (x7),
[index8] "b" (x8),
[indexa] "b" (xa),
[indexc] "b" (xc),
[indexe] "b" (xe)
: /* Clobbers */
"memory", "ctr",
"fr0","fr1","fr2","fr3",
"fr4","fr5","fr6","fr7"
) ;
return rc ;
}
/* Block store, using t0 and t1 as temporaries because we need to preserve the complete FPU context */
static void doublehummer_store_quads(void *dest, int count, const double *v0, const double *v1, double *t0, double *t1)
{
asm volatile (
"stfdx 0,0,%[t0]" nl
"lfdx 0,0,%[v0]" nl
"stfsdx 0,0,%[t1]" nl
"lfsdx 0,0,%[v1]" nl
"mtctr %[count]" nl
"0: stfpdx 0,0,%[dest]" nl
"addi %[dest],%[dest],16" nl
"bdnz 0b" nl
"lfdx 0,0,%[t0]" nl
"lfsdx 0,0,%[t1]" nl
: /* Outputs */
"=m" (*t0),
"=m" (*t1)
: /* Inputs */
[dest] "b" (dest),
[v0] "b" (v0),
[v1] "b" (v1),
[t0] "b" (t0),
[t1] "b" (t1),
[count] "r" (count)
: /* Clobbers */
"memory", "ctr"
) ;
}
/* Try a 'doublehummer' memcpy, return 0 if we could and 1 if we couldn't */
static int doublehummer_memcpy(void * dest, const void * src, unsigned long size)
{
if( k_exploit_doublehummer)
{
unsigned int di = (unsigned int) dest ;
unsigned int si = (unsigned int) src ;
unsigned int mutual_alignment = (di - si) & k_fpu_align_mask ;
unsigned int source_alignment = si & k_fpu_align_mask ;
unsigned int precopy_size = source_alignment ? (k_fpu_alignment - source_alignment) : 0 ;
unsigned int miniblock_di = di + precopy_size ;
unsigned int miniblock_si =si + precopy_size ;
unsigned int miniblock_size = size - precopy_size ;
unsigned int miniblock_count=miniblock_size/sizeof(miniblock) ;
unsigned int size_floor=miniblock_count*sizeof(miniblock) ;
unsigned int size_tail = size - size_floor - precopy_size ;
int rc ;
if( mutual_alignment )
{
dma_memcpy_statistic(k_copy_unaligned_rejects) ;
source_alignment_statistics[source_alignment] += 1 ;
dest_alignment_statistics[di & k_fpu_align_mask] += 1 ;
mutual_alignment_statistics[mutual_alignment] += 1 ;
return 1 ; // Alignment between source and destination not good enough
}
/* Using FPU in a FLIH is 'too hard' */
if(in_irq())
{
dma_memcpy_statistic(k_in_irq) ;
return 1 ;
}
/* Using FPU in a SLIH should be OK now we have an atomicity fix to problem in giveup_fpu */
if(in_softirq())
{
dma_memcpy_statistic(k_in_softirq) ;
if(k_inhibit_fpu_in_slih ) return 1 ;
}
/* The source and dest are mutually aligned. Do we need a 1-15 byte pre-copy to get to quad alignment ? */
if( precopy_size )
{
rc = __real__copy_tofrom_user(dest, src, precopy_size) ;
if(rc)
{
dma_memcpy_statistic(k_precopy_segv_trap) ;
return 1 ;
}
/* memcpy(dest,src,precopy_size) ; */
}
enable_kernel_fp() ;
/* The copy should work with interrupts enabled, but whenever I tried it there were occasional errors in copying. */
/* TODO: Diagnose why, fix, and run the copy without disabling. Same for the 'page copy' and 'page clear later */
if(k_force_mask || ( k_enable_mask && bgp_memcpy_control.mask))
{
unsigned long flags ;
local_irq_save(flags) ;
rc = doublehummer_copy_unroll((void *)miniblock_di,(void *)miniblock_si,miniblock_count-1) ;
local_irq_restore(flags) ;
}
else
{
rc = doublehummer_copy_unroll((void *)miniblock_di,(void *)miniblock_si,miniblock_count-1) ;
}
if( rc )
{
dma_memcpy_statistic(k_copy_segv_trap) ;
return 1 ;
}
if( size_tail )
{
/* TODO: Fix up what happens if this causes a 'segv' */
rc = __real__copy_tofrom_user((void *)(miniblock_di+size_floor), (void *)(miniblock_si+size_floor), size_tail) ;
if(rc)
{
dma_memcpy_statistic(k_postcopy_segv_trap) ;
return 1 ;
}
/* memcpy((void *)(miniblock_di+size_floor),(void *)(miniblock_si+size_floor),size_tail) ; */
}
if( k_verify_doublehummer && bgp_memcpy_control.verify_fpu)
{
verify_memcpy(dest,src,size) ;
}
return 0 ;
}
else
{
return 1 ;
}
}
static unsigned int operate_vcopy(unsigned long address, void * partner_vaddr, unsigned long size)
{
TRACEN(k_t_detail,"address=0x%08lx partner_vaddr=%p size=0x%08lx",address,partner_vaddr,size) ;
return doublehummer_memcpy(partner_vaddr,(const void *)address,size) ;
}
static int all_pages_mapped_read(unsigned long address, unsigned long size)
{
unsigned int start_page=(address >> k_page_shift) ;
unsigned int end_page=((address+size) >> k_page_shift) ;
unsigned int page_count = end_page-start_page+1 ;
unsigned int x ;
if( is_kernel_addr(address)) return 0 ; // If we have a 'kernel address', assume it's OK
if( k_inhibit_crosspage_read && page_count > 1 && 0 == bgp_memcpy_control.fpu_handle_pagecrossing_read)
{
/* TODO: Should be able to handle page-crossings, but have seen kernel traps related to this */
dma_memcpy_statistic(k_copy_crosspage_limitation_rejects) ;
return 1 ;
}
/* Defend against the possibility that the user application has posted an unmapped address */
for(x=0;x<page_count;x+=1)
{
int pageInt ;
int __user * pageIntP = (int __user *) ((start_page+x) << k_page_shift) ;
if( get_user(pageInt,pageIntP) )
{
TRACEN(k_t_general,"Unmapped : 0x%08x start_page=0x%08x page_count=0x%08x",((start_page+x) << k_page_shift),start_page,page_count) ;
if( k_diag_not_mapped)
{
tlb_t t ;
unsigned int r=v_to_r_maybe((void *)address, &t) ;
TRACEN(k_t_request,"Unmapped : 0x%08x start_page=0x%08x page_count=0x%08x",((start_page+x) << k_page_shift),start_page,page_count) ;
TRACEN(k_t_request,"address=0x%08lx r=0x%08x",address,r) ;
diagnose_tlb(&t) ;
}
return 1;
}
}
return 0 ;
}
static int all_pages_mapped_write(unsigned long address, unsigned long size)
{
unsigned int start_page=(address >> k_page_shift) ;
unsigned int end_page=((address+size) >> k_page_shift) ;
unsigned int page_count = end_page-start_page+1 ;
unsigned int x ;
/* int pageInt ; */
char __user * pageCharP = (char __user *) address ;
if( is_kernel_addr(address)) return 0 ; // If we have a 'kernel address', assume it's OK
if( k_inhibit_crosspage_write && page_count > 1 && 0 == bgp_memcpy_control.fpu_handle_pagecrossing_write )
{
/* TODO: Should be able to handle page-crossings, but have seen kernel traps related to this */
dma_memcpy_statistic(k_copy_crosspage_limitation_rejects) ;
return 1 ;
}
if(put_user(0,pageCharP))
{
TRACEN(k_t_general,"Unmapped : 0x%08x start_page=0x%08x page_count=0x%08x",((start_page+x) << k_page_shift),start_page,page_count) ;
if( k_diag_not_mapped)
{
tlb_t t ;
unsigned int r=v_to_r_maybe((void *)address, &t) ;
TRACEN(k_t_request,"Unmapped : 0x%08x start_page=0x%08x page_count=0x%08x",((start_page+x) << k_page_shift),start_page,page_count) ;
TRACEN(k_t_request,"address=0x%08lx r=0x%08x",address,r) ;
diagnose_tlb(&t) ;
}
return 1;
}
/* Defend against the possibility that the user application has posted an unmapped address */
for(x=1;x<page_count;x+=1)
{
/* int pageInt ; */
char __user * pageCharP = (char __user *) ((start_page+x) << k_page_shift) ;
/* put_user(current_injection_used, report) ; */
if( put_user(0,pageCharP) )
{
TRACEN(k_t_general,"Unmapped : 0x%08x start_page=0x%08x page_count=0x%08x",((start_page+x) << k_page_shift),start_page,page_count) ;
if( k_diag_not_mapped)
{
tlb_t t ;
unsigned int r=v_to_r_maybe((void *)address, &t) ;
TRACEN(k_t_request,"Unmapped : 0x%08x start_page=0x%08x page_count=0x%08x",((start_page+x) << k_page_shift),start_page,page_count) ;
TRACEN(k_t_request,"address=0x%08lx r=0x%08x",address,r) ;
diagnose_tlb(&t) ;
}
return 1;
}
}
return 0 ;
}
static int instrument_copy_user_address_by_touch(unsigned long address, unsigned long size,void * partner_vaddr)
{
if( k_map_read_check && all_pages_mapped_read(address,size))
{
dma_memcpy_statistic(k_copy_source_rejects) ;
return 1 ;
}
if( k_map_write_check && all_pages_mapped_write((unsigned int) partner_vaddr,size))
{
dma_memcpy_statistic(k_copy_target_rejects) ;
return 1 ;
}
/* Looks like we can run the transfer with the FPU */
return operate_vcopy(address,partner_vaddr,size) ;
}
static int instrument_copy_tofrom_user(unsigned long to, unsigned long from, unsigned long size)
{
int rc=1 ;
TRACEN(k_t_fpucopy,"(>)") ;
/* TODO: Check by touching and poking that all pages in 'to' and 'from' are appropriately mapped, before going into the hummer loop */
rc= instrument_copy_user_address_by_touch(from,size,(void *)to) ;
TRACEN(k_t_fpucopy,"(<) rc=%d",rc) ;
return rc ;
}
enum {
k_enable_dma_memcpy = 1 // TODO: Get DMA memcopy working, and enable it here
};
/* Returns 1 if we could DMA-copy things, 0 if we couldn't */
extern unsigned long bgp_fpu_instrument_copy_tofrom_user(void *to,
const void __user *from, unsigned long size)
{
if( k_premark && bgp_memcpy_control.verify_dma) memset(to,0x11,size) ; // Mark the memory so we know if we write it
// No advantage yet seen by using the DMA unit to do 'memcpy'
//#if defined(CONFIG_BLUEGENE_DMA_MEMCPY)
// if( k_enable_dma_memcpy && bgp_memcpy_control.use_dma)
// {
// if( bgp_memcpy_control.mask)
// {
// unsigned long flags ;
// unsigned long rc ;
// local_irq_save(flags) ;
// rc = bgp_dma_instrument_copy_tofrom_user(to, from, size) ;
// local_irq_restore(flags) ;
// return rc ;
// }
// else
// {
// return bgp_dma_instrument_copy_tofrom_user(to, from, size) ;
// }
// }
// else
//#endif
{
dma_memcpy_statistic(k_copy_tofrom_user_calls) ;
if( size > 0 && bgp_memcpy_control.use_fpu && size >= bgp_memcpy_control.fpu_threshold )
{
{
TRACEN(k_t_fpucopy,"to=%p from=%p size=0x%08lx",to,from,size) ;
{
unsigned long rc= instrument_copy_tofrom_user((unsigned long)to,(unsigned long)from,size) ;
dma_memcpy_statistic((0==rc) ? k_copy_accelerate_successes : k_copy_accelerate_rejects) ;
return rc ;
}
}
}
dma_memcpy_statistic(k_copy_size_rejects) ;
return 1 ; // Not copied, size under threshold
}
}
#if defined(CONFIG_WRAP_COPY_TOFROM_USER)
void copy_page(void *to, void *from)
{
TRACEN(k_t_fpucopy,"to=%p from=%p",to,from) ;
if(bgp_memcpy_control.assist_active )
{
unsigned int miniblock_count = k_page_size / sizeof(miniblock) ;
enable_kernel_fp() ;
if(k_force_mask || ( k_enable_mask && bgp_memcpy_control.mask))
{
unsigned long flags ;
local_irq_save(flags) ;
doublehummer_copy_unroll((void *)to,(void *)from,miniblock_count-1) ;
local_irq_restore(flags) ;
}
else
{
doublehummer_copy_unroll((void *)to,(void *)from,miniblock_count-1) ;
}
}
else
{
memcpy(to,from,k_page_size) ;
}
}
static const double v=0.0 ;
void clear_pages(void *p, int order)
{
TRACEN(k_t_fpucopy,"p=%p order=%d",p,order) ;
if(bgp_memcpy_control.assist_active )
{
unsigned int quadcount=(k_page_size/16) << order ;
double t0, t1 ;
enable_kernel_fp() ;
/* double v=0.0 ; */
if(k_force_mask || ( k_enable_mask && bgp_memcpy_control.mask))
{
unsigned long flags ;
local_irq_save(flags) ;
doublehummer_store_quads(p,quadcount,&v,&v, &t0, &t1) ;
local_irq_restore(flags) ;
}
else
{
doublehummer_store_quads(p,quadcount,&v,&v, &t0, &t1) ;
}
}
else
{
memset(p,0,k_page_size << order) ;
}
}
#endif
static void __init
bgp_fpu_register_memcpy_sysctl(void)
{
register_sysctl_paths(memcpy_ctl_path,bgp_memcpy_table) ;
TRACEN(k_t_init, "memcpy sysctl registered") ;
}
void __init
bgp_fpu_memcpy_init(void)
{
bgp_fpu_register_memcpy_sysctl() ;
}
module_init(bgp_fpu_memcpy_init);