mirror of
https://github.com/xen-project/xen.git
synced 2026-10-30 03:00:05 +01:00
bitkeeper revision 1.906 (40a5e91cnvIS_3gLwfnD2G3HV3odHA)
manual merge
This commit is contained in:
commit
5080a3f4b2
14 files changed
+769
-333
No files matched your search
@@ -144,6 +144,7 @@
|
||||
3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/i386/pci-i386.h
|
||||
3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/i386/pci-irq.c
|
||||
3ddb79bcZ_2FxINljqNSkqa17ISyJw xen/arch/i386/pci-pc.c
|
||||
40a4dfced2dnSzbKgJFlD3chKHexjQ xen/arch/i386/pdb-linux.c
|
||||
4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/i386/pdb-stub.c
|
||||
3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/i386/process.c
|
||||
3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/i386/rwlock.c
|
||||
|
||||
@@ -537,14 +537,7 @@ error_code:
|
||||
movl %edx,%es
|
||||
movl %edx,%fs
|
||||
movl %edx,%gs
|
||||
# We force a STI here. In most cases it is illegal to fault with
|
||||
# interrupts disabled, so no need to check EFLAGS. There is one
|
||||
# case when it /is/ valid -- on final return to guest context, we
|
||||
# CLI so we can atomically check for events to notify guest about and
|
||||
# return, all in one go. If we fault it is necessary to STI and the
|
||||
# worst that will happen is that our return code is no longer atomic.
|
||||
# This will do -- noone will ever notice. :-)
|
||||
sti
|
||||
movl %esp,%edx
|
||||
pushl %esi # push the error code
|
||||
pushl %edx # push the pt_regs pointer
|
||||
GET_CURRENT(%ebx)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
|
||||
/*
|
||||
* pervasive debugger
|
||||
* www.cl.cam.ac.uk/netos/pdb
|
||||
*
|
||||
* alex ho
|
||||
* 2004
|
||||
* university of cambridge computer laboratory
|
||||
*
|
||||
* linux & i386 dependent code. bleech.
|
||||
*/
|
||||
|
||||
#include <asm/pdb.h>
|
||||
|
||||
/* offset to the first instruction in the linux system call code
|
||||
where we can safely set a breakpoint */
|
||||
unsigned int pdb_linux_syscall_enter_bkpt_offset = 20;
|
||||
|
||||
/* offset to eflags saved on the stack after an int 80 */
|
||||
unsigned int pdb_linux_syscall_eflags_offset = 48;
|
||||
|
||||
/* offset to the instruction pointer saved on the stack after an int 80 */
|
||||
unsigned int pdb_linux_syscall_eip_offset = 40;
|
||||
|
||||
unsigned char
|
||||
pdb_linux_set_bkpt (unsigned long addr)
|
||||
{
|
||||
unsigned char old_instruction = *(unsigned char *)addr;
|
||||
*(unsigned char *)addr = 0xcc;
|
||||
return old_instruction;
|
||||
}
|
||||
|
||||
void
|
||||
pdb_linux_clr_bkpt (unsigned long addr, unsigned char value)
|
||||
{
|
||||
*(unsigned char *)addr = value;
|
||||
}
|
||||
|
||||
void
|
||||
pdb_linux_syscall_enter_bkpt (struct pt_regs *regs, long error_code,
|
||||
trap_info_t *ti)
|
||||
{
|
||||
/* set at breakpoint at the beginning of the
|
||||
system call in the target domain */
|
||||
|
||||
pdb_system_call_enter_instr = pdb_linux_set_bkpt(ti->address +
|
||||
pdb_linux_syscall_enter_bkpt_offset);
|
||||
pdb_system_call = 1;
|
||||
}
|
||||
|
||||
void
|
||||
pdb_linux_syscall_exit_bkpt (struct pt_regs *regs, struct pdb_context *pdb_ctx)
|
||||
{
|
||||
/*
|
||||
we've hit an int 0x80 in a user's program, jumped into xen
|
||||
(traps.c::do_general_protection()) which re-wrote the next
|
||||
instruction in the os kernel to 0xcc, and then hit that
|
||||
exception.
|
||||
|
||||
we need to re-write the return instruction in the user's
|
||||
program so that we know when we have finished the system call
|
||||
and are back in the user's program.
|
||||
|
||||
at this point our stack should look something like this:
|
||||
|
||||
esp = 0x80a59f0
|
||||
esp + 4 = 0x0
|
||||
esp + 8 = 0x80485a0
|
||||
esp + 12 = 0x2d
|
||||
esp + 16 = 0x80485f4
|
||||
esp + 20 = 0xbffffa48
|
||||
esp + 24 = 0xd
|
||||
esp + 28 = 0xc00a0833
|
||||
esp + 32 = 0x833
|
||||
esp + 36 = 0xd
|
||||
esp + 40 = 0x804dcdd saved eip
|
||||
esp + 44 = 0x82b saved cs
|
||||
esp + 48 = 0x213392 saved eflags
|
||||
esp + 52 = 0xbffffa2c saved esp
|
||||
esp + 56 = 0x833 saved ss
|
||||
esp + 60 = 0x1000000
|
||||
*/
|
||||
|
||||
/* restore the entry instruction for the system call */
|
||||
pdb_linux_clr_bkpt(regs->eip - 1, pdb_system_call_enter_instr);
|
||||
|
||||
/* save the address of eflags that was saved on the stack */
|
||||
pdb_system_call_eflags_addr = (regs->esp +
|
||||
pdb_linux_syscall_eflags_offset);
|
||||
|
||||
/* muck with the return instruction so that we trap back into the
|
||||
debugger when re-entering user space */
|
||||
pdb_system_call_next_addr = *(unsigned long *)(regs->esp +
|
||||
pdb_linux_syscall_eip_offset);
|
||||
pdb_linux_get_values (&pdb_system_call_leave_instr, 1,
|
||||
pdb_system_call_next_addr,
|
||||
pdb_ctx->process, pdb_ctx->ptbr);
|
||||
pdb_linux_set_values ("cc", 1, pdb_system_call_next_addr,
|
||||
pdb_ctx->process, pdb_ctx->ptbr);
|
||||
}
|
||||
+111
-63
@@ -47,26 +47,13 @@ static int pdb_in_buffer_ptr;
|
||||
static unsigned char pdb_in_checksum;
|
||||
static unsigned char pdb_xmit_checksum;
|
||||
|
||||
/* function pointers in the near future... */
|
||||
unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid);
|
||||
void pdb_linux_get_values(char *buffer, int length, unsigned long address,
|
||||
int pid, unsigned long cr3);
|
||||
void pdb_linux_set_values(char *buffer, int length, unsigned long address,
|
||||
int pid, unsigned long cr3);
|
||||
|
||||
struct pdb_context
|
||||
{
|
||||
int valid;
|
||||
int domain;
|
||||
int process;
|
||||
unsigned long ptbr; /* cached page table base register */
|
||||
};
|
||||
struct pdb_context pdb_ctx;
|
||||
|
||||
int pdb_continue_thread = 0;
|
||||
int pdb_general_thread = 0;
|
||||
|
||||
void pdb_put_packet (unsigned char *buffer, int ack);
|
||||
void pdb_bkpt_check (u_char *buffer, int length,
|
||||
unsigned long cr3, unsigned long addr);
|
||||
|
||||
int pdb_initialized = 0;
|
||||
int pdb_page_fault_possible = 0;
|
||||
@@ -75,6 +62,12 @@ int pdb_page_fault = 0;
|
||||
static int pdb_serhnd = -1;
|
||||
static int pdb_stepping = 0;
|
||||
|
||||
int pdb_system_call = 0;
|
||||
unsigned char pdb_system_call_enter_instr = 0; /* original enter instr */
|
||||
unsigned char pdb_system_call_leave_instr = 0; /* original next instr */
|
||||
unsigned long pdb_system_call_next_addr = 0; /* instr after int 0x80 */
|
||||
unsigned long pdb_system_call_eflags_addr = 0; /* saved eflags on stack */
|
||||
|
||||
static inline void pdb_put_char(unsigned char c)
|
||||
{
|
||||
serial_putc(pdb_serhnd, c);
|
||||
@@ -406,15 +399,49 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3,
|
||||
break;
|
||||
case 'S': /* step with signal */
|
||||
case 's': /* step */
|
||||
{
|
||||
if ( pdb_system_call_eflags_addr != 0 )
|
||||
{
|
||||
unsigned long eflags;
|
||||
char eflags_buf[sizeof(eflags)*2]; /* STUPID STUPID STUPID */
|
||||
|
||||
pdb_linux_get_values((u_char*)&eflags, sizeof(eflags),
|
||||
pdb_system_call_eflags_addr,
|
||||
pdb_ctx.process, pdb_ctx.ptbr);
|
||||
eflags |= X86_EFLAGS_TF;
|
||||
mem2hex ((u_char *)&eflags, eflags_buf, sizeof(eflags));
|
||||
pdb_linux_set_values(eflags_buf, sizeof(eflags),
|
||||
pdb_system_call_eflags_addr,
|
||||
pdb_ctx.process, pdb_ctx.ptbr);
|
||||
}
|
||||
|
||||
regs->eflags |= X86_EFLAGS_TF;
|
||||
pdb_stepping = 1;
|
||||
return 1;
|
||||
/* not reached */
|
||||
}
|
||||
case 'C': /* continue with signal */
|
||||
case 'c': /* continue */
|
||||
{
|
||||
if ( pdb_system_call_eflags_addr != 0 )
|
||||
{
|
||||
unsigned long eflags;
|
||||
char eflags_buf[sizeof(eflags)*2]; /* STUPID STUPID STUPID */
|
||||
|
||||
pdb_linux_get_values((u_char*)&eflags, sizeof(eflags),
|
||||
pdb_system_call_eflags_addr,
|
||||
pdb_ctx.process, pdb_ctx.ptbr);
|
||||
eflags &= ~X86_EFLAGS_TF;
|
||||
mem2hex ((u_char *)&eflags, eflags_buf, sizeof(eflags));
|
||||
pdb_linux_set_values(eflags_buf, sizeof(eflags),
|
||||
pdb_system_call_eflags_addr,
|
||||
pdb_ctx.process, pdb_ctx.ptbr);
|
||||
}
|
||||
|
||||
regs->eflags &= ~X86_EFLAGS_TF;
|
||||
return 1; /* jump out before replying to gdb */
|
||||
/* not reached */
|
||||
}
|
||||
case 'd':
|
||||
remote_debug = !(remote_debug); /* toggle debug flag */
|
||||
break;
|
||||
@@ -424,54 +451,11 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3,
|
||||
case 'g': /* return the value of the CPU registers */
|
||||
{
|
||||
pdb_x86_to_gdb_regs (pdb_out_buffer, regs);
|
||||
|
||||
/*
|
||||
printk (" reg: %s", pdb_out_buffer);
|
||||
printk ("\n");
|
||||
printk (" eax: 0x%08lx\n", regs->eax);
|
||||
printk (" ecx: 0x%08lx\n", regs->ecx);
|
||||
printk (" edx: 0x%08lx\n", regs->edx);
|
||||
printk (" ebx: 0x%08lx\n", regs->ebx);
|
||||
printk (" esp: 0x%08lx\n", regs->esp);
|
||||
printk (" ebp: 0x%08lx\n", regs->ebp);
|
||||
printk (" esi: 0x%08lx\n", regs->esi);
|
||||
printk (" edi: 0x%08lx\n", regs->edi);
|
||||
printk (" eip: 0x%08lx\n", regs->eip);
|
||||
printk (" efl: 0x%08lx\n", regs->eflags);
|
||||
printk (" xcs: 0x%08x\n", regs->xcs);
|
||||
printk (" xss: 0x%08x\n", regs->xss);
|
||||
printk (" xds: 0x%08x\n", regs->xds);
|
||||
printk (" xes: 0x%08x\n", regs->xes);
|
||||
printk (" xfs: 0x%08x\n", regs->xfs);
|
||||
printk (" xgs: 0x%08x\n", regs->xgs);
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
case 'G': /* set the value of the CPU registers - return OK */
|
||||
{
|
||||
pdb_gdb_to_x86_regs (regs, ptr);
|
||||
|
||||
/*
|
||||
printk (" ptr: %s \n\n", ptr);
|
||||
printk (" eax: 0x%08lx\n", regs->eax);
|
||||
printk (" ecx: 0x%08lx\n", regs->ecx);
|
||||
printk (" edx: 0x%08lx\n", regs->edx);
|
||||
printk (" ebx: 0x%08lx\n", regs->ebx);
|
||||
printk (" esp: 0x%08lx\n", regs->esp);
|
||||
printk (" ebp: 0x%08lx\n", regs->ebp);
|
||||
printk (" esi: 0x%08lx\n", regs->esi);
|
||||
printk (" edi: 0x%08lx\n", regs->edi);
|
||||
printk (" eip: 0x%08lx\n", regs->eip);
|
||||
printk (" efl: 0x%08lx\n", regs->eflags);
|
||||
printk (" xcs: 0x%08x\n", regs->xcs);
|
||||
printk (" xss: 0x%08x\n", regs->xss);
|
||||
printk (" xds: 0x%08x\n", regs->xds);
|
||||
printk (" xes: 0x%08x\n", regs->xes);
|
||||
printk (" xfs: 0x%08x\n", regs->xfs);
|
||||
printk (" xgs: 0x%08x\n", regs->xgs);
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
case 'H':
|
||||
@@ -572,17 +556,20 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3,
|
||||
if (addr >= PAGE_OFFSET)
|
||||
{
|
||||
hex2mem (ptr, (char *)addr, length);
|
||||
pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr);
|
||||
}
|
||||
else if (pdb_ctx.process != -1)
|
||||
{
|
||||
pdb_linux_set_values(ptr, length, addr,
|
||||
pdb_ctx.process,
|
||||
pdb_ctx.ptbr);
|
||||
pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
pdb_set_values (ptr, length,
|
||||
pdb_ctx.ptbr, addr);
|
||||
pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr);
|
||||
}
|
||||
pdb_page_fault_possible = 0;
|
||||
if (pdb_page_fault)
|
||||
@@ -936,7 +923,6 @@ int pdb_set_values(u_char *buffer, int length,
|
||||
unsigned long cr3, unsigned long addr)
|
||||
{
|
||||
int count = pdb_change_values(buffer, length, cr3, addr, __PDB_SET_VAL);
|
||||
pdb_bkpt_check(buffer, length, cr3, addr);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1176,16 +1162,35 @@ int pdb_handle_exception(int exceptionVector,
|
||||
|
||||
__asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : );
|
||||
|
||||
/* If the exception is an int3 from user space then pdb is only
|
||||
interested if it re-wrote an instruction set the breakpoint.
|
||||
This occurs when leaving a system call from a domain.
|
||||
*/
|
||||
if ( exceptionVector == 3 &&
|
||||
(xen_regs->xcs & 3) == 3 &&
|
||||
xen_regs->eip != pdb_system_call_next_addr + 1)
|
||||
{
|
||||
TRC(printf("pdb: user bkpt (0x%x) at 0x%x:0x%lx:0x%lx\n",
|
||||
exceptionVector, xen_regs->xcs & 3, cr3, xen_regs->eip));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If PDB didn't set the breakpoint, is not single stepping, and the user
|
||||
* didn't press the magic debug key, then we don't handle the exception.
|
||||
* If PDB didn't set the breakpoint, is not single stepping,
|
||||
* is not entering a system call in a domain,
|
||||
* the user didn't press the magic debug key,
|
||||
* then we don't handle the exception.
|
||||
*/
|
||||
bkpt = pdb_bkpt_search(cr3, xen_regs->eip - 1);
|
||||
if ( (bkpt == NULL) &&
|
||||
!pdb_stepping && (exceptionVector != KEYPRESS_EXCEPTION) &&
|
||||
!pdb_stepping &&
|
||||
!pdb_system_call &&
|
||||
xen_regs->eip != pdb_system_call_next_addr + 1 &&
|
||||
(exceptionVector != KEYPRESS_EXCEPTION) &&
|
||||
xen_regs->eip < 0xc0000000) /* xenolinux for now! */
|
||||
{
|
||||
TRC(printf("pdb: user bkpt at 0x%lx:0x%lx\n", cr3, xen_regs->eip));
|
||||
TRC(printf("pdb: user bkpt (0x%x) at 0x%lx:0x%lx\n",
|
||||
exceptionVector, cr3, xen_regs->eip));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1199,12 +1204,54 @@ int pdb_handle_exception(int exceptionVector,
|
||||
pdb_stepping = 0;
|
||||
}
|
||||
|
||||
if ( pdb_system_call )
|
||||
{
|
||||
pdb_system_call = 0;
|
||||
|
||||
pdb_linux_syscall_exit_bkpt (xen_regs, &pdb_ctx);
|
||||
|
||||
/* we don't have a saved breakpoint so we need to rewind eip */
|
||||
xen_regs->eip--;
|
||||
|
||||
/* if ther user doesn't care about breaking when entering a
|
||||
system call then we'll just ignore the exception */
|
||||
if ( (pdb_ctx.system_call & 0x01) == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( exceptionVector == BREAKPT_EXCEPTION && bkpt != NULL)
|
||||
{
|
||||
/* Executed Int3: replace breakpoint byte with real program byte. */
|
||||
xen_regs->eip--;
|
||||
}
|
||||
|
||||
/* returning to user space after a system call */
|
||||
if ( xen_regs->eip == pdb_system_call_next_addr + 1)
|
||||
{
|
||||
u_char instr[2]; /* REALLY REALLY REALLY STUPID */
|
||||
|
||||
mem2hex (&pdb_system_call_leave_instr, instr, sizeof(instr));
|
||||
|
||||
pdb_linux_set_values (instr, 1, pdb_system_call_next_addr,
|
||||
pdb_ctx.process, pdb_ctx.ptbr);
|
||||
|
||||
pdb_system_call_next_addr = 0;
|
||||
pdb_system_call_leave_instr = 0;
|
||||
|
||||
/* manually rewind eip */
|
||||
xen_regs->eip--;
|
||||
|
||||
/* if the user doesn't care about breaking when returning
|
||||
to user space after a system call then we'll just ignore
|
||||
the exception */
|
||||
if ( (pdb_ctx.system_call & 0x02) == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a signal for GDB. */
|
||||
switch ( exceptionVector )
|
||||
{
|
||||
@@ -1267,6 +1314,7 @@ void initialize_pdb()
|
||||
pdb_ctx.valid = 1;
|
||||
pdb_ctx.domain = -1;
|
||||
pdb_ctx.process = -1;
|
||||
pdb_ctx.system_call = 0;
|
||||
pdb_ctx.ptbr = 0;
|
||||
|
||||
printk("pdb: pervasive debugger (%s) www.cl.cam.ac.uk/netos/pdb\n",
|
||||
|
||||
+41
-21
@@ -20,15 +20,6 @@
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
/*
|
||||
* This lock must be acquired before sending a synchronous IPI to another
|
||||
* CPU (i.e., IPI + spin waiting for acknowledgement). The only safe ways of
|
||||
* acquiring the lock are spin_lock() and spin_trylock(). The former is only
|
||||
* safe if local interrupts are enabled (otherwise we will never see an IPI
|
||||
* destined for us which we must acknowledge for the lock to be released).
|
||||
*/
|
||||
static spinlock_t synchronous_ipi_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
/*
|
||||
* Some notes on x86 processor bugs affecting SMP operation:
|
||||
*
|
||||
@@ -220,16 +211,18 @@ static inline void send_IPI_allbutself(int vector)
|
||||
* 2) Leave the mm if we are in the lazy tlb mode.
|
||||
*/
|
||||
|
||||
static spinlock_t flush_lock = SPIN_LOCK_UNLOCKED;
|
||||
static volatile unsigned long flush_cpumask;
|
||||
#define FLUSH_ALL 0xffffffff
|
||||
|
||||
asmlinkage void smp_invalidate_interrupt(void)
|
||||
{
|
||||
ack_APIC_irq();
|
||||
local_flush_tlb();
|
||||
clear_bit(smp_processor_id(), &flush_cpumask);
|
||||
perfc_incrc(ipis);
|
||||
if ( likely(test_and_clear_bit(smp_processor_id(), &flush_cpumask)) )
|
||||
local_flush_tlb();
|
||||
}
|
||||
|
||||
#ifndef NO_DEVICES_IN_XEN
|
||||
int try_flush_tlb_mask(unsigned long mask)
|
||||
{
|
||||
if ( mask & (1 << smp_processor_id()) )
|
||||
@@ -240,7 +233,7 @@ int try_flush_tlb_mask(unsigned long mask)
|
||||
|
||||
if ( mask != 0 )
|
||||
{
|
||||
if ( unlikely(!spin_trylock(&synchronous_ipi_lock)) )
|
||||
if ( unlikely(!spin_trylock(&flush_lock)) )
|
||||
return 0;
|
||||
flush_cpumask = mask;
|
||||
send_IPI_mask(mask, INVALIDATE_TLB_VECTOR);
|
||||
@@ -249,15 +242,16 @@ int try_flush_tlb_mask(unsigned long mask)
|
||||
rep_nop();
|
||||
barrier();
|
||||
}
|
||||
spin_unlock(&synchronous_ipi_lock);
|
||||
spin_unlock(&flush_lock);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void flush_tlb_mask(unsigned long mask)
|
||||
{
|
||||
ASSERT(local_irq_is_enabled());
|
||||
ASSERT(!in_irq());
|
||||
|
||||
if ( mask & (1 << smp_processor_id()) )
|
||||
{
|
||||
@@ -267,7 +261,21 @@ void flush_tlb_mask(unsigned long mask)
|
||||
|
||||
if ( mask != 0 )
|
||||
{
|
||||
spin_lock(&synchronous_ipi_lock);
|
||||
/*
|
||||
* We are certainly not reentering a flush_lock region on this CPU
|
||||
* because we are not in an IRQ context. We can therefore wait for the
|
||||
* other guy to release the lock. This is harder than it sounds because
|
||||
* local interrupts might be disabled, and he may be waiting for us to
|
||||
* execute smp_invalidate_interrupt(). We deal with this possibility by
|
||||
* inlining the meat of that function here.
|
||||
*/
|
||||
while ( unlikely(!spin_trylock(&flush_lock)) )
|
||||
{
|
||||
if ( test_and_clear_bit(smp_processor_id(), &flush_cpumask) )
|
||||
local_flush_tlb();
|
||||
rep_nop();
|
||||
}
|
||||
|
||||
flush_cpumask = mask;
|
||||
send_IPI_mask(mask, INVALIDATE_TLB_VECTOR);
|
||||
while ( flush_cpumask != 0 )
|
||||
@@ -275,13 +283,15 @@ void flush_tlb_mask(unsigned long mask)
|
||||
rep_nop();
|
||||
barrier();
|
||||
}
|
||||
spin_unlock(&synchronous_ipi_lock);
|
||||
|
||||
spin_unlock(&flush_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void new_tlbflush_clock_period(void)
|
||||
{
|
||||
if ( unlikely(!spin_trylock(&synchronous_ipi_lock)) )
|
||||
/* Avoid deadlock because we might be reentering a flush_lock region. */
|
||||
if ( unlikely(!spin_trylock(&flush_lock)) )
|
||||
return;
|
||||
|
||||
/* Someone may acquire the lock and execute the flush before us. */
|
||||
@@ -304,7 +314,7 @@ void new_tlbflush_clock_period(void)
|
||||
tlbflush_clock++;
|
||||
|
||||
out:
|
||||
spin_unlock(&synchronous_ipi_lock);
|
||||
spin_unlock(&flush_lock);
|
||||
}
|
||||
|
||||
static void flush_tlb_all_pge_ipi(void* info)
|
||||
@@ -323,6 +333,12 @@ void smp_send_event_check_mask(unsigned long cpu_mask)
|
||||
send_IPI_mask(cpu_mask, EVENT_CHECK_VECTOR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure and data for smp_call_function(). This is designed to minimise
|
||||
* static memory requirements. It also looks cleaner.
|
||||
*/
|
||||
static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
struct call_data_struct {
|
||||
void (*func) (void *info);
|
||||
void *info;
|
||||
@@ -368,7 +384,8 @@ int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
|
||||
|
||||
ASSERT(local_irq_is_enabled());
|
||||
|
||||
spin_lock(&synchronous_ipi_lock);
|
||||
spin_lock(&call_lock);
|
||||
|
||||
call_data = &data;
|
||||
wmb();
|
||||
/* Send a message to all other CPUs and wait for them to respond */
|
||||
@@ -382,7 +399,7 @@ int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
|
||||
while (atomic_read(&data.finished) != cpus)
|
||||
barrier();
|
||||
|
||||
spin_unlock(&synchronous_ipi_lock);
|
||||
spin_unlock(&call_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -419,6 +436,7 @@ void smp_send_stop(void)
|
||||
asmlinkage void smp_event_check_interrupt(void)
|
||||
{
|
||||
ack_APIC_irq();
|
||||
perfc_incrc(ipis);
|
||||
}
|
||||
|
||||
asmlinkage void smp_call_function_interrupt(void)
|
||||
@@ -428,6 +446,8 @@ asmlinkage void smp_call_function_interrupt(void)
|
||||
int wait = call_data->wait;
|
||||
|
||||
ack_APIC_irq();
|
||||
perfc_incrc(ipis);
|
||||
|
||||
/*
|
||||
* Notify initiating CPU that I've grabbed the data and am
|
||||
* about to execute the function
|
||||
|
||||
+26
-31
@@ -107,36 +107,9 @@ static inline int kernel_text_address(unsigned long addr)
|
||||
|
||||
}
|
||||
|
||||
|
||||
void show_trace(unsigned long * stack)
|
||||
{
|
||||
int i;
|
||||
unsigned long addr;
|
||||
|
||||
printk("Call Trace: ");
|
||||
i = 1;
|
||||
while (((long) stack & (STACK_SIZE-1)) != 0) {
|
||||
addr = *stack++;
|
||||
if (kernel_text_address(addr)) {
|
||||
if (i && ((i % 6) == 0))
|
||||
printk("\n ");
|
||||
printk("[<%08lx>] ", addr);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
void show_traceX(void)
|
||||
{
|
||||
unsigned long *addr;
|
||||
__asm__ __volatile__ ("movl %%esp,%0" : "=r" (addr) : );
|
||||
show_trace(addr);
|
||||
}
|
||||
|
||||
void show_stack(unsigned long *esp)
|
||||
{
|
||||
unsigned long *stack;
|
||||
unsigned long *stack, addr;
|
||||
int i;
|
||||
|
||||
printk("Stack trace from ESP=%p:\n", esp);
|
||||
@@ -154,6 +127,20 @@ void show_stack(unsigned long *esp)
|
||||
printk("%08lx ", *stack++);
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
printk("Call Trace from ESP=%p: ", esp);
|
||||
stack = esp;
|
||||
i = 0;
|
||||
while (((long) stack & (STACK_SIZE-1)) != 0) {
|
||||
addr = *stack++;
|
||||
if (kernel_text_address(addr)) {
|
||||
if (i && ((i % 6) == 0))
|
||||
printk("\n ");
|
||||
printk("[<%08lx>] ", addr);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
void show_registers(struct pt_regs *regs)
|
||||
@@ -250,7 +237,6 @@ DO_ERROR_NOCODE( 0, "divide error", divide_error)
|
||||
DO_ERROR_NOCODE( 4, "overflow", overflow)
|
||||
DO_ERROR_NOCODE( 5, "bounds", bounds)
|
||||
DO_ERROR_NOCODE( 6, "invalid operand", invalid_op)
|
||||
DO_ERROR_NOCODE( 7, "device not available", device_not_available)
|
||||
DO_ERROR_NOCODE( 9, "coprocessor segment overrun", coprocessor_segment_overrun)
|
||||
DO_ERROR(10, "invalid TSS", invalid_TSS)
|
||||
DO_ERROR(11, "segment not present", segment_not_present)
|
||||
@@ -267,10 +253,10 @@ asmlinkage void do_int3(struct pt_regs *regs, long error_code)
|
||||
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
|
||||
trap_info_t *ti;
|
||||
|
||||
if ( pdb_handle_exception(3, regs) == 0 )
|
||||
return;
|
||||
if ( (regs->xcs & 3) != 3 )
|
||||
{
|
||||
if ( pdb_handle_exception(3, regs) == 0 )
|
||||
return;
|
||||
if ( unlikely((regs->xcs & 3) == 0) )
|
||||
{
|
||||
show_registers(regs);
|
||||
@@ -445,6 +431,15 @@ asmlinkage void do_general_protection(struct pt_regs *regs, long error_code)
|
||||
ti = current->thread.traps + (error_code>>3);
|
||||
if ( TI_GET_DPL(ti) >= (regs->xcs & 3) )
|
||||
{
|
||||
unsigned long cr3;
|
||||
|
||||
__asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : );
|
||||
if (pdb_initialized && pdb_ctx.system_call != 0 &&
|
||||
cr3 == pdb_ctx.ptbr)
|
||||
{
|
||||
pdb_linux_syscall_enter_bkpt(regs, error_code, ti);
|
||||
}
|
||||
|
||||
gtb->flags = GTBF_TRAP_NOCODE;
|
||||
regs->eip += 2;
|
||||
goto finish_propagation;
|
||||
|
||||
+9
-20
@@ -28,15 +28,6 @@
|
||||
|
||||
extern unsigned int alloc_new_dom_mem(struct task_struct *, unsigned int);
|
||||
|
||||
/* Basically used to protect the domain-id space. */
|
||||
static spinlock_t create_dom_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static domid_t get_domnr(void)
|
||||
{
|
||||
static domid_t domnr = 0;
|
||||
return ++domnr;
|
||||
}
|
||||
|
||||
static int msr_cpu_mask;
|
||||
static unsigned long msr_addr;
|
||||
static unsigned long msr_lo;
|
||||
@@ -117,23 +108,24 @@ long do_dom0_op(dom0_op_t *u_dom0_op)
|
||||
case DOM0_CREATEDOMAIN:
|
||||
{
|
||||
struct task_struct *p;
|
||||
static unsigned int pro = 0;
|
||||
static domid_t domnr = 0;
|
||||
static spinlock_t domnr_lock = SPIN_LOCK_UNLOCKED;
|
||||
unsigned int pro;
|
||||
domid_t dom;
|
||||
ret = -ENOMEM;
|
||||
|
||||
spin_lock_irq(&create_dom_lock);
|
||||
|
||||
if ( (dom = get_domnr()) == 0 )
|
||||
goto exit_create;
|
||||
spin_lock(&domnr_lock);
|
||||
dom = ++domnr;
|
||||
spin_unlock(&domnr_lock);
|
||||
|
||||
if (op->u.createdomain.cpu == -1 )
|
||||
pro = (pro+1) % smp_num_cpus;
|
||||
pro = (unsigned int)dom % smp_num_cpus;
|
||||
else
|
||||
pro = op->u.createdomain.cpu % smp_num_cpus;
|
||||
|
||||
p = do_createdomain(dom, pro);
|
||||
if ( p == NULL )
|
||||
goto exit_create;
|
||||
break;
|
||||
|
||||
if ( op->u.createdomain.name[0] )
|
||||
{
|
||||
@@ -145,16 +137,13 @@ long do_dom0_op(dom0_op_t *u_dom0_op)
|
||||
if ( ret != 0 )
|
||||
{
|
||||
__kill_domain(p);
|
||||
goto exit_create;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
op->u.createdomain.domain = p->domain;
|
||||
copy_to_user(u_dom0_op, op, sizeof(*op));
|
||||
|
||||
exit_create:
|
||||
spin_unlock_irq(&create_dom_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
+12
-4
@@ -292,6 +292,12 @@ struct pfn_info *alloc_domain_page(struct task_struct *p)
|
||||
unsigned long flags, mask, pfn_stamp, cpu_stamp;
|
||||
int i;
|
||||
|
||||
#ifdef NO_DEVICES_IN_XEN
|
||||
ASSERT(!in_irq());
|
||||
#else
|
||||
ASSERT((p != NULL) || !in_irq());
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&free_list_lock, flags);
|
||||
if ( likely(!list_empty(&free_list)) )
|
||||
{
|
||||
@@ -307,7 +313,7 @@ struct pfn_info *alloc_domain_page(struct task_struct *p)
|
||||
if ( (mask = page->u.cpu_mask) != 0 )
|
||||
{
|
||||
pfn_stamp = page->tlbflush_timestamp;
|
||||
for ( i = 0; (mask != 0) && (i < NR_CPUS); i++ )
|
||||
for ( i = 0; (mask != 0) && (i < smp_num_cpus); i++ )
|
||||
{
|
||||
if ( mask & (1<<i) )
|
||||
{
|
||||
@@ -319,11 +325,15 @@ struct pfn_info *alloc_domain_page(struct task_struct *p)
|
||||
|
||||
if ( unlikely(mask != 0) )
|
||||
{
|
||||
#ifdef NO_DEVICES_IN_XEN
|
||||
flush_tlb_mask(mask);
|
||||
#else
|
||||
/* In IRQ ctxt, flushing is best-effort only, to avoid deadlock. */
|
||||
if ( likely(!in_irq()) )
|
||||
flush_tlb_mask(mask);
|
||||
else if ( unlikely(!try_flush_tlb_mask(mask)) )
|
||||
goto free_and_exit;
|
||||
#endif
|
||||
perfc_incrc(need_flush_tlb_flush);
|
||||
}
|
||||
}
|
||||
@@ -332,7 +342,6 @@ struct pfn_info *alloc_domain_page(struct task_struct *p)
|
||||
page->type_and_flags = 0;
|
||||
if ( p != NULL )
|
||||
{
|
||||
ASSERT(!in_irq());
|
||||
wmb(); /* Domain pointer must be visible before updating refcnt. */
|
||||
spin_lock(&p->page_list_lock);
|
||||
if ( unlikely(p->tot_pages >= p->max_pages) )
|
||||
@@ -363,8 +372,7 @@ void free_domain_page(struct pfn_info *page)
|
||||
unsigned long flags;
|
||||
struct task_struct *p = page->u.domain;
|
||||
|
||||
if ( unlikely(in_irq()) )
|
||||
BUG();
|
||||
ASSERT(!in_irq());
|
||||
|
||||
if ( likely(!IS_XEN_HEAP_FRAME(page)) )
|
||||
{
|
||||
|
||||
+27
-19
@@ -804,6 +804,7 @@ static int do_extended_command(unsigned long ptr, unsigned long val)
|
||||
unsigned long pfn = ptr >> PAGE_SHIFT;
|
||||
unsigned long old_base_pfn;
|
||||
struct pfn_info *page = &frame_table[pfn];
|
||||
struct task_struct *p = current, *q;
|
||||
|
||||
switch ( cmd )
|
||||
{
|
||||
@@ -852,18 +853,18 @@ static int do_extended_command(unsigned long ptr, unsigned long val)
|
||||
break;
|
||||
|
||||
case MMUEXT_NEW_BASEPTR:
|
||||
okay = get_page_and_type_from_pagenr(pfn, PGT_l2_page_table, current);
|
||||
okay = get_page_and_type_from_pagenr(pfn, PGT_l2_page_table, p);
|
||||
if ( likely(okay) )
|
||||
{
|
||||
invalidate_shadow_ldt();
|
||||
|
||||
percpu_info[cpu].deferred_ops &= ~DOP_FLUSH_TLB;
|
||||
old_base_pfn = pagetable_val(current->mm.pagetable) >> PAGE_SHIFT;
|
||||
current->mm.pagetable = mk_pagetable(pfn << PAGE_SHIFT);
|
||||
old_base_pfn = pagetable_val(p->mm.pagetable) >> PAGE_SHIFT;
|
||||
p->mm.pagetable = mk_pagetable(pfn << PAGE_SHIFT);
|
||||
|
||||
shadow_mk_pagetable(¤t->mm);
|
||||
shadow_mk_pagetable(&p->mm);
|
||||
|
||||
write_ptbase(¤t->mm);
|
||||
write_ptbase(&p->mm);
|
||||
|
||||
put_page_and_type(&frame_table[old_base_pfn]);
|
||||
|
||||
@@ -899,13 +900,13 @@ static int do_extended_command(unsigned long ptr, unsigned long val)
|
||||
okay = 0;
|
||||
MEM_LOG("Bad args to SET_LDT: ptr=%08lx, ents=%08lx", ptr, ents);
|
||||
}
|
||||
else if ( (current->mm.ldt_ents != ents) ||
|
||||
(current->mm.ldt_base != ptr) )
|
||||
else if ( (p->mm.ldt_ents != ents) ||
|
||||
(p->mm.ldt_base != ptr) )
|
||||
{
|
||||
invalidate_shadow_ldt();
|
||||
current->mm.ldt_base = ptr;
|
||||
current->mm.ldt_ents = ents;
|
||||
load_LDT(current);
|
||||
p->mm.ldt_base = ptr;
|
||||
p->mm.ldt_ents = ents;
|
||||
load_LDT(p);
|
||||
percpu_info[cpu].deferred_ops &= ~DOP_RELOAD_LDT;
|
||||
if ( ents != 0 )
|
||||
percpu_info[cpu].deferred_ops |= DOP_RELOAD_LDT;
|
||||
@@ -921,10 +922,10 @@ static int do_extended_command(unsigned long ptr, unsigned long val)
|
||||
percpu_info[cpu].subject_id |=
|
||||
((domid_t)((ptr&~0xFFFF)|(val>>16)))<<32;
|
||||
|
||||
if ( !IS_PRIV(current) )
|
||||
if ( !IS_PRIV(p) )
|
||||
{
|
||||
MEM_LOG("Dom %llu has no privilege to set subject domain",
|
||||
current->domain);
|
||||
p->domain);
|
||||
okay = 0;
|
||||
}
|
||||
else
|
||||
@@ -943,19 +944,26 @@ static int do_extended_command(unsigned long ptr, unsigned long val)
|
||||
}
|
||||
break;
|
||||
|
||||
/* XXX This function is racey! */
|
||||
case MMUEXT_REASSIGN_PAGE:
|
||||
if ( unlikely(!IS_PRIV(current)) )
|
||||
if ( unlikely(!IS_PRIV(p)) )
|
||||
{
|
||||
MEM_LOG("Dom %llu has no privilege to reassign page ownership",
|
||||
current->domain);
|
||||
p->domain);
|
||||
okay = 0;
|
||||
}
|
||||
else if ( likely(percpu_info[cpu].gps != NULL) )
|
||||
else if ( likely((q = percpu_info[cpu].gps) != NULL) &&
|
||||
likely(test_bit(_PGC_allocated, &page->count_and_flags)) &&
|
||||
likely(page->u.domain == p) ) /* won't be smp-guest safe */
|
||||
{
|
||||
current->tot_pages--;
|
||||
percpu_info[cpu].gps->tot_pages++;
|
||||
page->u.domain = percpu_info[cpu].gps;
|
||||
spin_lock(&p->page_list_lock);
|
||||
p->tot_pages--;
|
||||
list_del(&page->list);
|
||||
spin_unlock(&p->page_list_lock);
|
||||
page->u.domain = q;
|
||||
spin_lock(&q->page_list_lock);
|
||||
q->tot_pages++;
|
||||
list_add_tail(&page->list, &q->page_list);
|
||||
spin_unlock(&q->page_list_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <xen/list.h>
|
||||
#include <hypervisor-ifs/dom0_ops.h>
|
||||
#include <hypervisor-ifs/hypervisor-if.h> /* for domain id */
|
||||
|
||||
extern int pdb_initialized;
|
||||
@@ -37,6 +38,17 @@ extern int pdb_handle_exception(int exceptionVector,
|
||||
extern int pdb_serial_input(u_char c, struct pt_regs *regs);
|
||||
extern void pdb_do_debug(dom0_op_t *op);
|
||||
|
||||
/* PDB Context. */
|
||||
struct pdb_context
|
||||
{
|
||||
int valid;
|
||||
int domain;
|
||||
int process;
|
||||
int system_call; /* 0x01 break on enter, 0x02 break on exit */
|
||||
unsigned long ptbr;
|
||||
};
|
||||
extern struct pdb_context pdb_ctx;
|
||||
|
||||
/* Breakpoints. */
|
||||
struct pdb_breakpoint
|
||||
{
|
||||
@@ -56,4 +68,21 @@ extern char *mem2hex (char *, char *, int);
|
||||
extern char *hex2mem (char *, char *, int);
|
||||
extern int hexToInt (char **ptr, int *intValue);
|
||||
|
||||
/* Temporary Linux specific definitions */
|
||||
extern int pdb_system_call;
|
||||
extern unsigned char pdb_system_call_enter_instr; /* original enter instr */
|
||||
extern unsigned char pdb_system_call_leave_instr; /* original next instr */
|
||||
extern unsigned long pdb_system_call_next_addr; /* instr after int 0x80 */
|
||||
extern unsigned long pdb_system_call_eflags_addr; /* saved eflags on stack */
|
||||
|
||||
unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid);
|
||||
void pdb_linux_get_values(char *buffer, int length, unsigned long address,
|
||||
int pid, unsigned long cr3);
|
||||
void pdb_linux_set_values(char *buffer, int length, unsigned long address,
|
||||
int pid, unsigned long cr3);
|
||||
void pdb_linux_syscall_enter_bkpt (struct pt_regs *regs, long error_code,
|
||||
trap_info_t *ti);
|
||||
void pdb_linux_syscall_exit_bkpt (struct pt_regs *regs,
|
||||
struct pdb_context *pdb_ctx);
|
||||
|
||||
#endif /* __PDB_H__ */
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/desc.h>
|
||||
#include <asm/flushtlb.h>
|
||||
#include <asm/pdb.h>
|
||||
#include <xen/config.h>
|
||||
#include <xen/spinlock.h>
|
||||
#include <hypervisor-ifs/hypervisor-if.h>
|
||||
@@ -406,8 +407,9 @@ extern struct desc_struct *idt_tables[];
|
||||
0, 8))
|
||||
|
||||
#define SET_FAST_TRAP(_p) \
|
||||
(memcpy(idt_tables[smp_processor_id()] + (_p)->fast_trap_idx, \
|
||||
&((_p)->fast_trap_desc), 8))
|
||||
(pdb_initialized ? (void *) 0 : \
|
||||
(memcpy(idt_tables[smp_processor_id()] + (_p)->fast_trap_idx, \
|
||||
&((_p)->fast_trap_desc), 8)))
|
||||
|
||||
long set_fast_trap(struct task_struct *p, int idx);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
PERFCOUNTER_CPU( irqs, "#interrupts" )
|
||||
PERFCOUNTER_CPU( ipis, "#IPIs" )
|
||||
PERFCOUNTER_CPU( irq_time, "cycles spent in irq handler" )
|
||||
|
||||
PERFCOUNTER_CPU( apic_timer, "apic timer interrupts" )
|
||||
|
||||
@@ -13,19 +13,33 @@
|
||||
#include "common.h"
|
||||
#include <asm/hypervisor-ifs/dom_mem_ops.h>
|
||||
|
||||
static void net_tx_action(unsigned long unused);
|
||||
static void netif_page_release(struct page *page);
|
||||
static void make_tx_response(netif_t *netif,
|
||||
u16 id,
|
||||
s8 st);
|
||||
static void make_rx_response(netif_t *netif,
|
||||
static int make_rx_response(netif_t *netif,
|
||||
u16 id,
|
||||
s8 st,
|
||||
netif_addr_t addr,
|
||||
u16 size);
|
||||
|
||||
static void net_tx_action(unsigned long unused);
|
||||
static DECLARE_TASKLET(net_tx_tasklet, net_tx_action, 0);
|
||||
|
||||
static void net_rx_action(unsigned long unused);
|
||||
static DECLARE_TASKLET(net_rx_tasklet, net_rx_action, 0);
|
||||
|
||||
typedef struct {
|
||||
u16 id;
|
||||
unsigned long old_mach_ptr;
|
||||
unsigned long new_mach_pfn;
|
||||
netif_t *netif;
|
||||
} rx_info_t;
|
||||
static struct sk_buff_head rx_queue;
|
||||
static multicall_entry_t rx_mcl[NETIF_RX_RING_SIZE*2];
|
||||
static mmu_update_t rx_mmu[NETIF_RX_RING_SIZE*4];
|
||||
static unsigned char rx_notify[NR_EVENT_CHANNELS];
|
||||
|
||||
/* Don't currently gate addition of an interface to the tx scheduling list. */
|
||||
#define tx_work_exists(_if) (1)
|
||||
|
||||
@@ -38,12 +52,24 @@ static unsigned long mmap_vstart;
|
||||
static u16 pending_id[MAX_PENDING_REQS];
|
||||
static netif_t *pending_netif[MAX_PENDING_REQS];
|
||||
static u16 pending_ring[MAX_PENDING_REQS];
|
||||
static spinlock_t pend_prod_lock = SPIN_LOCK_UNLOCKED;
|
||||
typedef unsigned int PEND_RING_IDX;
|
||||
#define MASK_PEND_IDX(_i) ((_i)&(MAX_PENDING_REQS-1))
|
||||
static PEND_RING_IDX pending_prod, pending_cons;
|
||||
#define NR_PENDING_REQS (MAX_PENDING_REQS - pending_prod + pending_cons)
|
||||
|
||||
/* Freed TX SKBs get batched on this ring before return to pending_ring. */
|
||||
static u16 dealloc_ring[MAX_PENDING_REQS];
|
||||
static spinlock_t dealloc_lock = SPIN_LOCK_UNLOCKED;
|
||||
static PEND_RING_IDX dealloc_prod, dealloc_cons;
|
||||
|
||||
typedef struct {
|
||||
u16 idx;
|
||||
netif_tx_request_t req;
|
||||
netif_t *netif;
|
||||
} tx_info_t;
|
||||
static struct sk_buff_head tx_queue;
|
||||
static multicall_entry_t tx_mcl[MAX_PENDING_REQS];
|
||||
|
||||
static struct list_head net_schedule_list;
|
||||
static spinlock_t net_schedule_list_lock;
|
||||
|
||||
@@ -98,22 +124,12 @@ static inline void maybe_schedule_tx_action(void)
|
||||
int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
netif_t *netif = (netif_t *)dev->priv;
|
||||
s8 status = NETIF_RSP_OKAY;
|
||||
u16 size=0, id;
|
||||
mmu_update_t mmu[4];
|
||||
multicall_entry_t mcl[2];
|
||||
unsigned long vdata, mdata=0, new_mfn;
|
||||
|
||||
/* Drop the packet if the target domain has no receive buffers. */
|
||||
if ( (netif->rx_req_cons == netif->rx->req_prod) ||
|
||||
((netif->rx_req_cons-netif->rx_resp_prod) == NETIF_RX_RING_SIZE) )
|
||||
{
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
goto drop;
|
||||
|
||||
id = netif->rx->ring[MASK_NETIF_RX_IDX(netif->rx_req_cons++)].req.id;
|
||||
|
||||
/*
|
||||
* We do not copy the packet unless:
|
||||
* 1. The data is shared; or
|
||||
@@ -130,11 +146,7 @@ int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
struct sk_buff *nskb = alloc_skb(PAGE_SIZE-1024, GFP_ATOMIC);
|
||||
int hlen = skb->data - skb->head;
|
||||
if ( unlikely(nskb == NULL) )
|
||||
{
|
||||
DPRINTK("DOM%llu couldn't get memory for skb.\n", netif->domid);
|
||||
status = NETIF_RSP_ERROR;
|
||||
goto out;
|
||||
}
|
||||
goto drop;
|
||||
skb_reserve(nskb, hlen);
|
||||
__skb_put(nskb, skb->len);
|
||||
(void)skb_copy_bits(skb, -hlen, nskb->head, hlen + skb->len);
|
||||
@@ -142,65 +154,166 @@ int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
skb = nskb;
|
||||
}
|
||||
|
||||
vdata = (unsigned long)skb->data;
|
||||
mdata = virt_to_machine(vdata);
|
||||
size = skb->tail - skb->data;
|
||||
((rx_info_t *)&skb->cb[0])->id =
|
||||
netif->rx->ring[MASK_NETIF_RX_IDX(netif->rx_req_cons++)].req.id;
|
||||
((rx_info_t *)&skb->cb[0])->netif = netif;
|
||||
|
||||
__skb_queue_tail(&rx_queue, skb);
|
||||
tasklet_schedule(&net_rx_tasklet);
|
||||
|
||||
new_mfn = get_new_mfn();
|
||||
return 0;
|
||||
|
||||
mmu[0].ptr = (new_mfn << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE;
|
||||
mmu[0].val = __pa(vdata) >> PAGE_SHIFT;
|
||||
|
||||
mmu[1].val = (unsigned long)(netif->domid<<16) & ~0xFFFFUL;
|
||||
mmu[1].ptr = (unsigned long)(netif->domid<< 0) & ~0xFFFFUL;
|
||||
mmu[2].val = (unsigned long)(netif->domid>>16) & ~0xFFFFUL;
|
||||
mmu[2].ptr = (unsigned long)(netif->domid>>32) & ~0xFFFFUL;
|
||||
mmu[1].ptr |= MMU_EXTENDED_COMMAND;
|
||||
mmu[1].val |= MMUEXT_SET_SUBJECTDOM_L;
|
||||
mmu[2].ptr |= MMU_EXTENDED_COMMAND;
|
||||
mmu[2].val |= MMUEXT_SET_SUBJECTDOM_H;
|
||||
|
||||
mmu[3].ptr = (mdata & PAGE_MASK) | MMU_EXTENDED_COMMAND;
|
||||
mmu[3].val = MMUEXT_REASSIGN_PAGE;
|
||||
|
||||
mcl[0].op = __HYPERVISOR_mmu_update;
|
||||
mcl[0].args[0] = (unsigned long)mmu;
|
||||
mcl[0].args[1] = 4;
|
||||
mcl[1].op = __HYPERVISOR_update_va_mapping;
|
||||
mcl[1].args[0] = vdata >> PAGE_SHIFT;
|
||||
mcl[1].args[1] = (new_mfn << PAGE_SHIFT) | __PAGE_KERNEL;
|
||||
mcl[1].args[2] = UVMF_INVLPG;
|
||||
|
||||
(void)HYPERVISOR_multicall(mcl, 2);
|
||||
if ( mcl[0].args[5] != 0 )
|
||||
{
|
||||
DPRINTK("Failed MMU update transferring to DOM%llu\n", netif->domid);
|
||||
(void)HYPERVISOR_update_va_mapping(
|
||||
vdata >> PAGE_SHIFT,
|
||||
(pte_t) { (mdata & PAGE_MASK) | __PAGE_KERNEL },
|
||||
UVMF_INVLPG);
|
||||
dealloc_mfn(new_mfn);
|
||||
status = NETIF_RSP_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
phys_to_machine_mapping[__pa(vdata) >> PAGE_SHIFT] = new_mfn;
|
||||
|
||||
atomic_set(&(skb_shinfo(skb)->dataref), 1);
|
||||
skb_shinfo(skb)->nr_frags = 0;
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
netif->stats.rx_bytes += size;
|
||||
netif->stats.rx_packets++;
|
||||
|
||||
out:
|
||||
spin_lock(&netif->rx_lock);
|
||||
make_rx_response(netif, id, status, mdata, size);
|
||||
spin_unlock(&netif->rx_lock);
|
||||
drop:
|
||||
netif->stats.rx_dropped++;
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void xen_network_done_notify(void)
|
||||
{
|
||||
static struct net_device *eth0_dev = NULL;
|
||||
if ( unlikely(eth0_dev == NULL) )
|
||||
eth0_dev = __dev_get_by_name("eth0");
|
||||
netif_rx_schedule(eth0_dev);
|
||||
}
|
||||
/*
|
||||
* Add following to poll() function in NAPI driver (Tigon3 is example):
|
||||
* if ( xen_network_done() )
|
||||
* tge_3nable_ints(tp);
|
||||
*/
|
||||
int xen_network_done(void)
|
||||
{
|
||||
return skb_queue_empty(&rx_queue);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void net_rx_action(unsigned long unused)
|
||||
{
|
||||
netif_t *netif;
|
||||
s8 status;
|
||||
u16 size, id, evtchn;
|
||||
mmu_update_t *mmu = rx_mmu;
|
||||
multicall_entry_t *mcl;
|
||||
unsigned long vdata, mdata, new_mfn;
|
||||
struct sk_buff_head rxq;
|
||||
struct sk_buff *skb;
|
||||
u16 notify_list[NETIF_RX_RING_SIZE];
|
||||
int notify_nr = 0;
|
||||
|
||||
skb_queue_head_init(&rxq);
|
||||
|
||||
mcl = rx_mcl;
|
||||
while ( (skb = __skb_dequeue(&rx_queue)) != NULL )
|
||||
{
|
||||
netif = ((rx_info_t *)&skb->cb[0])->netif;
|
||||
vdata = (unsigned long)skb->data;
|
||||
mdata = virt_to_machine(vdata);
|
||||
new_mfn = get_new_mfn();
|
||||
|
||||
mmu[0].ptr = (new_mfn << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE;
|
||||
mmu[0].val = __pa(vdata) >> PAGE_SHIFT;
|
||||
mmu[1].val = (unsigned long)(netif->domid<<16) & ~0xFFFFUL;
|
||||
mmu[1].ptr = (unsigned long)(netif->domid<< 0) & ~0xFFFFUL;
|
||||
mmu[2].val = (unsigned long)(netif->domid>>16) & ~0xFFFFUL;
|
||||
mmu[2].ptr = (unsigned long)(netif->domid>>32) & ~0xFFFFUL;
|
||||
mmu[1].ptr |= MMU_EXTENDED_COMMAND;
|
||||
mmu[1].val |= MMUEXT_SET_SUBJECTDOM_L;
|
||||
mmu[2].ptr |= MMU_EXTENDED_COMMAND;
|
||||
mmu[2].val |= MMUEXT_SET_SUBJECTDOM_H;
|
||||
mmu[3].ptr = (mdata & PAGE_MASK) | MMU_EXTENDED_COMMAND;
|
||||
mmu[3].val = MMUEXT_REASSIGN_PAGE;
|
||||
|
||||
mcl[0].op = __HYPERVISOR_update_va_mapping;
|
||||
mcl[0].args[0] = vdata >> PAGE_SHIFT;
|
||||
mcl[0].args[1] = (new_mfn << PAGE_SHIFT) | __PAGE_KERNEL;
|
||||
mcl[0].args[2] = 0;
|
||||
mcl[1].op = __HYPERVISOR_mmu_update;
|
||||
mcl[1].args[0] = (unsigned long)mmu;
|
||||
mcl[1].args[1] = 4;
|
||||
|
||||
mmu += 4;
|
||||
mcl += 2;
|
||||
|
||||
((rx_info_t *)&skb->cb[0])->old_mach_ptr = mdata;
|
||||
((rx_info_t *)&skb->cb[0])->new_mach_pfn = new_mfn;
|
||||
__skb_queue_tail(&rxq, skb);
|
||||
|
||||
/* Filled the batch queue? */
|
||||
if ( (mcl - rx_mcl) == ARRAY_SIZE(rx_mcl) )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( mcl == rx_mcl )
|
||||
return;
|
||||
|
||||
mcl[-2].args[2] = UVMF_FLUSH_TLB;
|
||||
(void)HYPERVISOR_multicall(rx_mcl, mcl - rx_mcl);
|
||||
|
||||
mcl = rx_mcl;
|
||||
while ( (skb = __skb_dequeue(&rxq)) != NULL )
|
||||
{
|
||||
netif = ((rx_info_t *)&skb->cb[0])->netif;
|
||||
size = skb->tail - skb->data;
|
||||
id = ((rx_info_t *)&skb->cb[0])->id;
|
||||
new_mfn = ((rx_info_t *)&skb->cb[0])->new_mach_pfn;
|
||||
mdata = ((rx_info_t *)&skb->cb[0])->old_mach_ptr;
|
||||
|
||||
/* Check the reassignment error code. */
|
||||
if ( unlikely(mcl[1].args[5] != 0) )
|
||||
{
|
||||
DPRINTK("Failed MMU update transferring to DOM%llu\n",
|
||||
netif->domid);
|
||||
(void)HYPERVISOR_update_va_mapping(
|
||||
(unsigned long)skb->head >> PAGE_SHIFT,
|
||||
(pte_t) { (mdata & PAGE_MASK) | __PAGE_KERNEL },
|
||||
UVMF_INVLPG);
|
||||
dealloc_mfn(new_mfn);
|
||||
status = NETIF_RSP_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
phys_to_machine_mapping[__pa(skb->data) >> PAGE_SHIFT] = new_mfn;
|
||||
|
||||
atomic_set(&(skb_shinfo(skb)->dataref), 1);
|
||||
skb_shinfo(skb)->nr_frags = 0;
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
netif->stats.rx_bytes += size;
|
||||
netif->stats.rx_packets++;
|
||||
|
||||
status = NETIF_RSP_OKAY;
|
||||
}
|
||||
|
||||
evtchn = netif->evtchn;
|
||||
if ( make_rx_response(netif, id, status, mdata, size) &&
|
||||
(rx_notify[evtchn] == 0) )
|
||||
{
|
||||
rx_notify[evtchn] = 1;
|
||||
notify_list[notify_nr++] = evtchn;
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
mcl += 2;
|
||||
}
|
||||
|
||||
while ( notify_nr != 0 )
|
||||
{
|
||||
evtchn = notify_list[--notify_nr];
|
||||
rx_notify[evtchn] = 0;
|
||||
notify_via_evtchn(evtchn);
|
||||
}
|
||||
|
||||
/* More work to do? */
|
||||
if ( !skb_queue_empty(&rx_queue) )
|
||||
tasklet_schedule(&net_rx_tasklet);
|
||||
#if 0
|
||||
else
|
||||
xen_network_done_notify();
|
||||
#endif
|
||||
}
|
||||
|
||||
struct net_device_stats *netif_be_get_stats(struct net_device *dev)
|
||||
{
|
||||
netif_t *netif = dev->priv;
|
||||
@@ -215,10 +328,12 @@ static int __on_net_schedule_list(netif_t *netif)
|
||||
static void remove_from_net_schedule_list(netif_t *netif)
|
||||
{
|
||||
spin_lock(&net_schedule_list_lock);
|
||||
ASSERT(__on_net_schedule_list(netif));
|
||||
list_del(&netif->list);
|
||||
netif->list.next = NULL;
|
||||
netif_put(netif);
|
||||
if ( likely(__on_net_schedule_list(netif)) )
|
||||
{
|
||||
list_del(&netif->list);
|
||||
netif->list.next = NULL;
|
||||
netif_put(netif);
|
||||
}
|
||||
spin_unlock(&net_schedule_list_lock);
|
||||
}
|
||||
|
||||
@@ -269,7 +384,51 @@ static void net_tx_action(unsigned long unused)
|
||||
u16 pending_idx;
|
||||
NETIF_RING_IDX i;
|
||||
struct page *page;
|
||||
multicall_entry_t *mcl;
|
||||
|
||||
if ( (i = dealloc_cons) == dealloc_prod )
|
||||
goto skip_dealloc;
|
||||
|
||||
mcl = tx_mcl;
|
||||
while ( i != dealloc_prod )
|
||||
{
|
||||
pending_idx = dealloc_ring[MASK_PEND_IDX(i++)];
|
||||
mcl[0].op = __HYPERVISOR_update_va_mapping;
|
||||
mcl[0].args[0] = MMAP_VADDR(pending_idx) >> PAGE_SHIFT;
|
||||
mcl[0].args[1] = 0;
|
||||
mcl[0].args[2] = 0;
|
||||
mcl++;
|
||||
}
|
||||
|
||||
mcl[-1].args[2] = UVMF_FLUSH_TLB;
|
||||
(void)HYPERVISOR_multicall(tx_mcl, mcl - tx_mcl);
|
||||
|
||||
while ( dealloc_cons != dealloc_prod )
|
||||
{
|
||||
pending_idx = dealloc_ring[MASK_PEND_IDX(dealloc_cons++)];
|
||||
|
||||
netif = pending_netif[pending_idx];
|
||||
|
||||
spin_lock(&netif->tx_lock);
|
||||
make_tx_response(netif, pending_id[pending_idx], NETIF_RSP_OKAY);
|
||||
spin_unlock(&netif->tx_lock);
|
||||
|
||||
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
|
||||
|
||||
/*
|
||||
* Scheduling checks must happen after the above response is posted.
|
||||
* This avoids a possible race with a guest OS on another CPU.
|
||||
*/
|
||||
mb();
|
||||
if ( (netif->tx_req_cons != netif->tx->req_prod) &&
|
||||
((netif->tx_req_cons-netif->tx_resp_prod) != NETIF_TX_RING_SIZE) )
|
||||
add_to_net_schedule_list_tail(netif);
|
||||
|
||||
netif_put(netif);
|
||||
}
|
||||
|
||||
skip_dealloc:
|
||||
mcl = tx_mcl;
|
||||
while ( (NR_PENDING_REQS < MAX_PENDING_REQS) &&
|
||||
!list_empty(&net_schedule_list) )
|
||||
{
|
||||
@@ -340,29 +499,61 @@ static void net_tx_action(unsigned long unused)
|
||||
|
||||
pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)];
|
||||
|
||||
if ( HYPERVISOR_update_va_mapping_otherdomain(
|
||||
MMAP_VADDR(pending_idx) >> PAGE_SHIFT,
|
||||
(pte_t) { (txreq.addr & PAGE_MASK) | __PAGE_KERNEL },
|
||||
0, netif->domid) != 0 )
|
||||
{
|
||||
DPRINTK("Bad page frame\n");
|
||||
make_tx_response(netif, txreq.id, NETIF_RSP_ERROR);
|
||||
netif_put(netif);
|
||||
continue;
|
||||
}
|
||||
phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx)) >> PAGE_SHIFT] =
|
||||
txreq.addr >> PAGE_SHIFT;
|
||||
|
||||
if ( unlikely((skb = alloc_skb(PKT_PROT_LEN, GFP_ATOMIC)) == NULL) )
|
||||
{
|
||||
DPRINTK("Can't allocate a skb in start_xmit.\n");
|
||||
make_tx_response(netif, txreq.id, NETIF_RSP_ERROR);
|
||||
netif_put(netif);
|
||||
HYPERVISOR_update_va_mapping(MMAP_VADDR(pending_idx) >> PAGE_SHIFT,
|
||||
(pte_t) { 0 }, UVMF_INVLPG);
|
||||
break;
|
||||
}
|
||||
|
||||
mcl[0].op = __HYPERVISOR_update_va_mapping_otherdomain;
|
||||
mcl[0].args[0] = MMAP_VADDR(pending_idx) >> PAGE_SHIFT;
|
||||
mcl[0].args[1] = (txreq.addr & PAGE_MASK) | __PAGE_KERNEL;
|
||||
mcl[0].args[2] = 0;
|
||||
mcl[0].args[3] = (unsigned long)netif->domid;
|
||||
mcl[0].args[4] = (unsigned long)(netif->domid>>32);
|
||||
mcl++;
|
||||
|
||||
((tx_info_t *)&skb->cb[0])->idx = pending_idx;
|
||||
((tx_info_t *)&skb->cb[0])->netif = netif;
|
||||
memcpy(&((tx_info_t *)&skb->cb[0])->req, &txreq, sizeof(txreq));
|
||||
__skb_queue_tail(&tx_queue, skb);
|
||||
|
||||
pending_cons++;
|
||||
|
||||
/* Filled the batch queue? */
|
||||
if ( (mcl - tx_mcl) == ARRAY_SIZE(tx_mcl) )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( mcl == tx_mcl )
|
||||
return;
|
||||
|
||||
(void)HYPERVISOR_multicall(tx_mcl, mcl - tx_mcl);
|
||||
|
||||
mcl = tx_mcl;
|
||||
while ( (skb = __skb_dequeue(&tx_queue)) != NULL )
|
||||
{
|
||||
pending_idx = ((tx_info_t *)&skb->cb[0])->idx;
|
||||
netif = ((tx_info_t *)&skb->cb[0])->netif;
|
||||
memcpy(&txreq, &((tx_info_t *)&skb->cb[0])->req, sizeof(txreq));
|
||||
|
||||
/* Check the remap error code. */
|
||||
if ( unlikely(mcl[0].args[5] != 0) )
|
||||
{
|
||||
DPRINTK("Bad page frame\n");
|
||||
make_tx_response(netif, txreq.id, NETIF_RSP_ERROR);
|
||||
netif_put(netif);
|
||||
kfree_skb(skb);
|
||||
mcl++;
|
||||
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx)) >> PAGE_SHIFT] =
|
||||
txreq.addr >> PAGE_SHIFT;
|
||||
|
||||
__skb_put(skb, PKT_PROT_LEN);
|
||||
memcpy(skb->data,
|
||||
(void *)(MMAP_VADDR(pending_idx)|(txreq.addr&~PAGE_MASK)),
|
||||
@@ -391,42 +582,23 @@ static void net_tx_action(unsigned long unused)
|
||||
netif->stats.tx_bytes += txreq.size;
|
||||
netif->stats.tx_packets++;
|
||||
|
||||
pending_cons++;
|
||||
|
||||
netif_rx(skb);
|
||||
netif->dev->last_rx = jiffies;
|
||||
|
||||
mcl++;
|
||||
}
|
||||
}
|
||||
|
||||
static void netif_page_release(struct page *page)
|
||||
{
|
||||
unsigned long flags;
|
||||
netif_t *netif;
|
||||
u16 pending_idx;
|
||||
u16 pending_idx = page - virt_to_page(mmap_vstart);
|
||||
|
||||
pending_idx = page - virt_to_page(mmap_vstart);
|
||||
spin_lock_irqsave(&dealloc_lock, flags);
|
||||
dealloc_ring[MASK_PEND_IDX(dealloc_prod++)] = pending_idx;
|
||||
spin_unlock_irqrestore(&dealloc_lock, flags);
|
||||
|
||||
netif = pending_netif[pending_idx];
|
||||
|
||||
HYPERVISOR_update_va_mapping(MMAP_VADDR(pending_idx) >> PAGE_SHIFT,
|
||||
(pte_t) { 0 }, UVMF_INVLPG);
|
||||
|
||||
spin_lock(&netif->tx_lock);
|
||||
make_tx_response(netif, pending_id[pending_idx], NETIF_RSP_OKAY);
|
||||
spin_unlock(&netif->tx_lock);
|
||||
|
||||
/*
|
||||
* Scheduling checks must happen after the above response is posted.
|
||||
* This avoids a possible race with a guest OS on another CPU.
|
||||
*/
|
||||
mb();
|
||||
netif_schedule_work(netif);
|
||||
|
||||
netif_put(netif);
|
||||
|
||||
spin_lock_irqsave(&pend_prod_lock, flags);
|
||||
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
|
||||
spin_unlock_irqrestore(&pend_prod_lock, flags);
|
||||
tasklet_schedule(&net_tx_tasklet);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -497,11 +669,11 @@ static void make_tx_response(netif_t *netif,
|
||||
notify_via_evtchn(netif->evtchn);
|
||||
}
|
||||
|
||||
static void make_rx_response(netif_t *netif,
|
||||
u16 id,
|
||||
s8 st,
|
||||
netif_addr_t addr,
|
||||
u16 size)
|
||||
static int make_rx_response(netif_t *netif,
|
||||
u16 id,
|
||||
s8 st,
|
||||
netif_addr_t addr,
|
||||
u16 size)
|
||||
{
|
||||
NET_RING_IDX i = netif->rx_resp_prod;
|
||||
netif_rx_response_t *resp;
|
||||
@@ -516,8 +688,7 @@ static void make_rx_response(netif_t *netif,
|
||||
netif->rx->resp_prod = netif->rx_resp_prod = ++i;
|
||||
|
||||
mb(); /* Update producer before checking event threshold. */
|
||||
if ( i == netif->rx->event )
|
||||
notify_via_evtchn(netif->evtchn);
|
||||
return (i == netif->rx->event);
|
||||
}
|
||||
|
||||
static int __init init_module(void)
|
||||
@@ -527,6 +698,9 @@ static int __init init_module(void)
|
||||
if ( !(start_info.flags & SIF_INITDOMAIN) )
|
||||
return 0;
|
||||
|
||||
skb_queue_head_init(&rx_queue);
|
||||
skb_queue_head_init(&tx_queue);
|
||||
|
||||
netif_interface_init();
|
||||
|
||||
if ( (mmap_vstart = allocate_empty_lowmem_region(MAX_PENDING_REQS)) == 0 )
|
||||
|
||||
@@ -37,6 +37,10 @@ static void network_tx_buf_gc(struct net_device *dev);
|
||||
static void network_alloc_rx_buffers(struct net_device *dev);
|
||||
static void cleanup_module(void);
|
||||
|
||||
static unsigned long rx_pfn_array[NETIF_RX_RING_SIZE];
|
||||
static multicall_entry_t rx_mcl[NETIF_RX_RING_SIZE+1];
|
||||
static mmu_update_t rx_mmu[NETIF_RX_RING_SIZE];
|
||||
|
||||
static struct list_head dev_list;
|
||||
|
||||
struct net_private
|
||||
@@ -178,8 +182,7 @@ static void network_alloc_rx_buffers(struct net_device *dev)
|
||||
struct sk_buff *skb;
|
||||
NETIF_RING_IDX i = np->rx->req_prod;
|
||||
dom_mem_op_t op;
|
||||
unsigned long pfn_array[NETIF_RX_RING_SIZE];
|
||||
int ret, nr_pfns = 0;
|
||||
int nr_pfns = 0;
|
||||
|
||||
/* Make sure the batch is large enough to be worthwhile (1/2 ring). */
|
||||
if ( unlikely((i - np->rx_resp_cons) > (NETIF_RX_RING_SIZE/2)) ||
|
||||
@@ -201,9 +204,14 @@ static void network_alloc_rx_buffers(struct net_device *dev)
|
||||
|
||||
np->rx->ring[MASK_NET_RX_IDX(i)].req.id = id;
|
||||
|
||||
pfn_array[nr_pfns++] = virt_to_machine(skb->head) >> PAGE_SHIFT;
|
||||
HYPERVISOR_update_va_mapping((unsigned long)skb->head >> PAGE_SHIFT,
|
||||
(pte_t) { 0 }, UVMF_INVLPG);
|
||||
rx_pfn_array[nr_pfns] = virt_to_machine(skb->head) >> PAGE_SHIFT;
|
||||
|
||||
rx_mcl[nr_pfns].op = __HYPERVISOR_update_va_mapping;
|
||||
rx_mcl[nr_pfns].args[0] = (unsigned long)skb->head >> PAGE_SHIFT;
|
||||
rx_mcl[nr_pfns].args[1] = 0;
|
||||
rx_mcl[nr_pfns].args[2] = 0;
|
||||
|
||||
nr_pfns++;
|
||||
}
|
||||
while ( (++i - np->rx_resp_cons) != NETIF_RX_RING_SIZE );
|
||||
|
||||
@@ -213,14 +221,22 @@ static void network_alloc_rx_buffers(struct net_device *dev)
|
||||
*/
|
||||
flush_page_update_queue();
|
||||
|
||||
/* After all PTEs have been zapped we blow away stale TLB entries. */
|
||||
rx_mcl[nr_pfns-1].args[2] = UVMF_FLUSH_TLB;
|
||||
|
||||
/* Give away a batch of pages. */
|
||||
op.op = MEMOP_RESERVATION_DECREASE;
|
||||
op.u.decrease.size = nr_pfns;
|
||||
op.u.decrease.pages = pfn_array;
|
||||
if ( (ret = HYPERVISOR_dom_mem_op(&op)) != nr_pfns )
|
||||
{
|
||||
printk(KERN_WARNING "Unable to reduce memory reservation (%d)\n", ret);
|
||||
BUG();
|
||||
}
|
||||
op.u.decrease.pages = rx_pfn_array;
|
||||
rx_mcl[nr_pfns].op = __HYPERVISOR_dom_mem_op;
|
||||
rx_mcl[nr_pfns].args[0] = (unsigned long)&op;
|
||||
|
||||
/* Zap PTEs and give away pages in one big multicall. */
|
||||
(void)HYPERVISOR_multicall(rx_mcl, nr_pfns+1);
|
||||
|
||||
/* Check return status of HYPERVISOR_dom_mem_op(). */
|
||||
if ( rx_mcl[nr_pfns].args[5] != nr_pfns )
|
||||
panic("Unable to reduce memory reservation\n");
|
||||
|
||||
np->rx->req_prod = i;
|
||||
}
|
||||
@@ -295,17 +311,36 @@ static void netif_int(int irq, void *dev_id, struct pt_regs *ptregs)
|
||||
struct net_device *dev = dev_id;
|
||||
struct net_private *np = dev->priv;
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
netif_rx_response_t *rx;
|
||||
NETIF_RING_IDX i;
|
||||
mmu_update_t mmu;
|
||||
|
||||
spin_lock_irqsave(&np->tx_lock, flags);
|
||||
network_tx_buf_gc(dev);
|
||||
spin_unlock_irqrestore(&np->tx_lock, flags);
|
||||
|
||||
again:
|
||||
for ( i = np->rx_resp_cons; i != np->rx->resp_prod; i++ )
|
||||
if ( np->rx_resp_cons != np->rx->resp_prod )
|
||||
netif_rx_schedule(dev);
|
||||
}
|
||||
|
||||
|
||||
static int netif_poll(struct net_device *dev, int *pbudget)
|
||||
{
|
||||
struct net_private *np = dev->priv;
|
||||
struct sk_buff *skb;
|
||||
netif_rx_response_t *rx;
|
||||
NETIF_RING_IDX i;
|
||||
mmu_update_t *mmu = rx_mmu;
|
||||
multicall_entry_t *mcl = rx_mcl;
|
||||
int work_done, budget, more_to_do = 1;
|
||||
struct sk_buff_head rxq;
|
||||
unsigned long flags;
|
||||
|
||||
skb_queue_head_init(&rxq);
|
||||
|
||||
if ( (budget = *pbudget) > dev->quota )
|
||||
budget = dev->quota;
|
||||
|
||||
for ( i = np->rx_resp_cons, work_done = 0;
|
||||
(i != np->rx->resp_prod) && (work_done < budget);
|
||||
i++, work_done++ )
|
||||
{
|
||||
rx = &np->rx->ring[MASK_NET_RX_IDX(i)].resp;
|
||||
|
||||
@@ -317,38 +352,53 @@ static void netif_int(int irq, void *dev_id, struct pt_regs *ptregs)
|
||||
/* Gate this error. We get a (valid) slew of them on suspend. */
|
||||
if ( np->state == NETIF_STATE_ACTIVE )
|
||||
printk(KERN_ALERT "bad buffer on RX ring!(%d)\n", rx->status);
|
||||
dev_kfree_skb_any(skb);
|
||||
dev_kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
||||
skb->data = skb->tail = skb->head + (rx->addr & ~PAGE_MASK);
|
||||
skb_put(skb, rx->status);
|
||||
|
||||
np->stats.rx_packets++;
|
||||
np->stats.rx_bytes += rx->status;
|
||||
|
||||
/* Remap the page. */
|
||||
mmu.ptr = (rx->addr & PAGE_MASK) | MMU_MACHPHYS_UPDATE;
|
||||
mmu.val = __pa(skb->head) >> PAGE_SHIFT;
|
||||
if ( HYPERVISOR_mmu_update(&mmu, 1) != 0 )
|
||||
BUG();
|
||||
HYPERVISOR_update_va_mapping((unsigned long)skb->head >> PAGE_SHIFT,
|
||||
(pte_t) { (rx->addr & PAGE_MASK) |
|
||||
__PAGE_KERNEL },
|
||||
0);
|
||||
mmu->ptr = (rx->addr & PAGE_MASK) | MMU_MACHPHYS_UPDATE;
|
||||
mmu->val = __pa(skb->head) >> PAGE_SHIFT;
|
||||
mmu++;
|
||||
mcl->op = __HYPERVISOR_update_va_mapping;
|
||||
mcl->args[0] = (unsigned long)skb->head >> PAGE_SHIFT;
|
||||
mcl->args[1] = (rx->addr & PAGE_MASK) | __PAGE_KERNEL;
|
||||
mcl->args[2] = 0;
|
||||
mcl++;
|
||||
|
||||
phys_to_machine_mapping[__pa(skb->head) >> PAGE_SHIFT] =
|
||||
rx->addr >> PAGE_SHIFT;
|
||||
|
||||
/*
|
||||
* Set up shinfo -- from alloc_skb This was particularily nasty: the
|
||||
* shared info is hidden at the back of the data area (presumably so it
|
||||
* can be shared), but on page flip it gets very spunked.
|
||||
*/
|
||||
__skb_queue_tail(&rxq, skb);
|
||||
}
|
||||
|
||||
/* Do all the remapping work, and M->P updates, in one big hypercall. */
|
||||
if ( likely((mcl - rx_mcl) != 0) )
|
||||
{
|
||||
mcl->op = __HYPERVISOR_mmu_update;
|
||||
mcl->args[0] = (unsigned long)rx_mmu;
|
||||
mcl->args[1] = mmu - rx_mmu;
|
||||
mcl++;
|
||||
(void)HYPERVISOR_multicall(rx_mcl, mcl - rx_mcl);
|
||||
}
|
||||
|
||||
while ( (skb = __skb_dequeue(&rxq)) != NULL )
|
||||
{
|
||||
/* Set the shared-info area, which is hidden behind the real data. */
|
||||
atomic_set(&(skb_shinfo(skb)->dataref), 1);
|
||||
skb_shinfo(skb)->nr_frags = 0;
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
skb->data = skb->tail = skb->head + (rx->addr & ~PAGE_MASK);
|
||||
skb_put(skb, rx->status);
|
||||
/* Ethernet-specific work. Delayed to here as it peeks the header. */
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
||||
np->stats.rx_packets++;
|
||||
|
||||
np->stats.rx_bytes += rx->status;
|
||||
/* Pass it up. */
|
||||
netif_rx(skb);
|
||||
dev->last_rx = jiffies;
|
||||
}
|
||||
@@ -356,12 +406,28 @@ static void netif_int(int irq, void *dev_id, struct pt_regs *ptregs)
|
||||
np->rx_resp_cons = i;
|
||||
|
||||
network_alloc_rx_buffers(dev);
|
||||
np->rx->event = np->rx_resp_cons + 1;
|
||||
|
||||
*pbudget -= work_done;
|
||||
dev->quota -= work_done;
|
||||
|
||||
if ( work_done < budget )
|
||||
{
|
||||
local_irq_save(flags);
|
||||
|
||||
np->rx->event = i + 1;
|
||||
|
||||
/* Deal with hypervisor racing our resetting of rx_event. */
|
||||
mb();
|
||||
if ( np->rx->resp_prod != i )
|
||||
goto again;
|
||||
/* Deal with hypervisor racing our resetting of rx_event. */
|
||||
mb();
|
||||
if ( np->rx->resp_prod == i )
|
||||
{
|
||||
__netif_rx_complete(dev);
|
||||
more_to_do = 0;
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
return more_to_do;
|
||||
}
|
||||
|
||||
|
||||
@@ -524,6 +590,8 @@ static int __init init_module(void)
|
||||
dev->hard_start_xmit = network_start_xmit;
|
||||
dev->stop = network_close;
|
||||
dev->get_stats = network_get_stats;
|
||||
dev->poll = netif_poll;
|
||||
dev->weight = 64;
|
||||
|
||||
if ( (err = register_netdev(dev)) != 0 )
|
||||
{
|
||||
|
||||