aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/ppc/kernel/cputable.c5
-rw-r--r--arch/sparc64/kernel/dtlb_base.S14
-rw-r--r--arch/sparc64/kernel/dtlb_prot.S12
-rw-r--r--arch/sparc64/kernel/head.S61
-rw-r--r--arch/sparc64/kernel/itlb_base.S26
-rw-r--r--arch/sparc64/kernel/ktlb.S92
-rw-r--r--arch/sparc64/mm/init.c187
-rw-r--r--arch/um/drivers/Makefile2
-rw-r--r--arch/um/drivers/ubd_kern.c556
-rw-r--r--arch/um/drivers/ubd_user.c75
-rw-r--r--arch/um/include/aio.h18
-rw-r--r--arch/um/include/os.h5
-rw-r--r--arch/um/os-Linux/aio.c205
13 files changed, 657 insertions, 601 deletions
diff --git a/arch/ppc/kernel/cputable.c b/arch/ppc/kernel/cputable.c
index 546e1ea4caf..6b76cf58d9e 100644
--- a/arch/ppc/kernel/cputable.c
+++ b/arch/ppc/kernel/cputable.c
@@ -91,7 +91,7 @@ struct cpu_spec cpu_specs[] = {
.cpu_features = CPU_FTR_COMMON | CPU_FTR_601 |
CPU_FTR_HPTE_TABLE,
.cpu_user_features = COMMON_PPC | PPC_FEATURE_601_INSTR |
- PPC_FEATURE_UNIFIED_CACHE,
+ PPC_FEATURE_UNIFIED_CACHE | PPC_FEATURE_NO_TB,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_601
@@ -745,7 +745,8 @@ struct cpu_spec cpu_specs[] = {
.cpu_name = "403GCX",
.cpu_features = CPU_FTR_SPLIT_ID_CACHE |
CPU_FTR_USE_TB,
- .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
+ .cpu_user_features = PPC_FEATURE_32 |
+ PPC_FEATURE_HAS_MMU | PPC_FEATURE_NO_TB,
.icache_bsize = 16,
.dcache_bsize = 16,
},
diff --git a/arch/sparc64/kernel/dtlb_base.S b/arch/sparc64/kernel/dtlb_base.S
index 702d349c1e8..6528786840c 100644
--- a/arch/sparc64/kernel/dtlb_base.S
+++ b/arch/sparc64/kernel/dtlb_base.S
@@ -53,19 +53,18 @@
* be guaranteed to be 0 ... mmu_context.h does guarantee this
* by only using 10 bits in the hwcontext value.
*/
-#define CREATE_VPTE_OFFSET1(r1, r2)
+#define CREATE_VPTE_OFFSET1(r1, r2) nop
#define CREATE_VPTE_OFFSET2(r1, r2) \
srax r1, 10, r2
-#define CREATE_VPTE_NOP nop
#else
#define CREATE_VPTE_OFFSET1(r1, r2) \
srax r1, PAGE_SHIFT, r2
#define CREATE_VPTE_OFFSET2(r1, r2) \
sllx r2, 3, r2
-#define CREATE_VPTE_NOP
#endif
/* DTLB ** ICACHE line 1: Quick user TLB misses */
+ mov TLB_SFSR, %g1
ldxa [%g1 + %g1] ASI_DMMU, %g4 ! Get TAG_ACCESS
andcc %g4, TAG_CONTEXT_BITS, %g0 ! From Nucleus?
from_tl1_trap:
@@ -74,18 +73,16 @@ from_tl1_trap:
be,pn %xcc, kvmap ! Yep, special processing
CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset
cmp %g5, 4 ! Last trap level?
- be,pn %xcc, longpath ! Yep, cannot risk VPTE miss
- nop ! delay slot
/* DTLB ** ICACHE line 2: User finish + quick kernel TLB misses */
+ be,pn %xcc, longpath ! Yep, cannot risk VPTE miss
+ nop ! delay slot
ldxa [%g3 + %g6] ASI_S, %g5 ! Load VPTE
1: brgez,pn %g5, longpath ! Invalid, branch out
nop ! Delay-slot
9: stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
retry ! Trap return
nop
- nop
- nop
/* DTLB ** ICACHE line 3: winfixups+real_faults */
longpath:
@@ -106,8 +103,7 @@ longpath:
nop
nop
nop
- CREATE_VPTE_NOP
+ nop
#undef CREATE_VPTE_OFFSET1
#undef CREATE_VPTE_OFFSET2
-#undef CREATE_VPTE_NOP
diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S
index d848bb7374b..e0a92016260 100644
--- a/arch/sparc64/kernel/dtlb_prot.S
+++ b/arch/sparc64/kernel/dtlb_prot.S
@@ -14,14 +14,14 @@
*/
/* PROT ** ICACHE line 1: User DTLB protection trap */
- stxa %g0, [%g1] ASI_DMMU ! Clear SFSR FaultValid bit
- membar #Sync ! Synchronize ASI stores
- rdpr %pstate, %g5 ! Move into alternate globals
+ mov TLB_SFSR, %g1
+ stxa %g0, [%g1] ASI_DMMU ! Clear FaultValid bit
+ membar #Sync ! Synchronize stores
+ rdpr %pstate, %g5 ! Move into alt-globals
wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
- rdpr %tl, %g1 ! Need to do a winfixup?
+ rdpr %tl, %g1 ! Need a winfixup?
cmp %g1, 1 ! Trap level >1?
- mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr
- nop
+ mov TLB_TAG_ACCESS, %g4 ! For reload of vaddr
/* PROT ** ICACHE line 2: More real fault processing */
bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
index 4c942f71184..b49dcd4504b 100644
--- a/arch/sparc64/kernel/head.S
+++ b/arch/sparc64/kernel/head.S
@@ -28,19 +28,14 @@
#include <asm/mmu.h>
/* This section from from _start to sparc64_boot_end should fit into
- * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space
- * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to
- * 0x0000.0000.0040.6000 and empty_bad_page, which is from
- * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000.
+ * 0x0000000000404000 to 0x0000000000408000.
*/
-
.text
.globl start, _start, stext, _stext
_start:
start:
_stext:
stext:
-bootup_user_stack:
! 0x0000000000404000
b sparc64_boot
flushw /* Flush register file. */
@@ -392,31 +387,30 @@ tlb_fixup_done:
* former does use this code, the latter does not yet due
* to some complexities. That should be fixed up at some
* point.
+ *
+ * There used to be enormous complexity wrt. transferring
+ * over from the firwmare's trap table to the Linux kernel's.
+ * For example, there was a chicken & egg problem wrt. building
+ * the OBP page tables, yet needing to be on the Linux kernel
+ * trap table (to translate PAGE_OFFSET addresses) in order to
+ * do that.
+ *
+ * We now handle OBP tlb misses differently, via linear lookups
+ * into the prom_trans[] array. So that specific problem no
+ * longer exists. Yet, unfortunately there are still some issues
+ * preventing trampoline.S from using this code... ho hum.
*/
.globl setup_trap_table
setup_trap_table:
save %sp, -192, %sp
- /* Force interrupts to be disabled. Transferring over to
- * the Linux trap table is a very delicate operation.
- * Until we are actually on the Linux trap table, we cannot
- * get the PAGE_OFFSET linear mappings translated. We need
- * that mapping to be setup in order to initialize the firmware
- * page tables.
- *
- * So there is this window of time, from the return from
- * prom_set_trap_table() until inherit_prom_mappings_post()
- * (in arch/sparc64/mm/init.c) completes, during which no
- * firmware address space accesses can be made.
- */
+ /* Force interrupts to be disabled. */
rdpr %pstate, %o1
andn %o1, PSTATE_IE, %o1
wrpr %o1, 0x0, %pstate
wrpr %g0, 15, %pil
- /* Ok, now make the final valid firmware call to jump over
- * to the Linux trap table.
- */
+ /* Make the firmware call to jump over to the Linux trap table. */
call prom_set_trap_table
sethi %hi(sparc64_ttable_tl0), %o0
@@ -540,15 +534,21 @@ setup_tba: /* i0 = is_starfire */
ret
restore
+sparc64_boot_end:
+
+#include "systbls.S"
+#include "ktlb.S"
+#include "etrap.S"
+#include "rtrap.S"
+#include "winfixup.S"
+#include "entry.S"
/*
- * The following skips make sure the trap table in ttable.S is aligned
+ * The following skip makes sure the trap table in ttable.S is aligned
* on a 32K boundary as required by the v9 specs for TBA register.
*/
-sparc64_boot_end:
- .skip 0x2000 + _start - sparc64_boot_end
-bootup_user_stack_end:
- .skip 0x2000
+1:
+ .skip 0x4000 + _start - 1b
#ifdef CONFIG_SBUS
/* This is just a hack to fool make depend config.h discovering
@@ -560,15 +560,6 @@ bootup_user_stack_end:
! 0x0000000000408000
#include "ttable.S"
-#include "systbls.S"
-#include "ktlb.S"
-#include "etrap.S"
-#include "rtrap.S"
-#include "winfixup.S"
-#include "entry.S"
-
- /* This is just anal retentiveness on my part... */
- .align 16384
.data
.align 8
diff --git a/arch/sparc64/kernel/itlb_base.S b/arch/sparc64/kernel/itlb_base.S
index b5e32dfa4fb..4951ff8f687 100644
--- a/arch/sparc64/kernel/itlb_base.S
+++ b/arch/sparc64/kernel/itlb_base.S
@@ -15,14 +15,12 @@
*/
#define CREATE_VPTE_OFFSET1(r1, r2) \
srax r1, 10, r2
-#define CREATE_VPTE_OFFSET2(r1, r2)
-#define CREATE_VPTE_NOP nop
+#define CREATE_VPTE_OFFSET2(r1, r2) nop
#else /* PAGE_SHIFT */
#define CREATE_VPTE_OFFSET1(r1, r2) \
srax r1, PAGE_SHIFT, r2
#define CREATE_VPTE_OFFSET2(r1, r2) \
sllx r2, 3, r2
-#define CREATE_VPTE_NOP
#endif /* PAGE_SHIFT */
@@ -36,6 +34,7 @@
*/
/* ITLB ** ICACHE line 1: Quick user TLB misses */
+ mov TLB_SFSR, %g1
ldxa [%g1 + %g1] ASI_IMMU, %g4 ! Get TAG_ACCESS
CREATE_VPTE_OFFSET1(%g4, %g6) ! Create VPTE offset
CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset
@@ -43,41 +42,38 @@
1: brgez,pn %g5, 3f ! Not valid, branch out
sethi %hi(_PAGE_EXEC), %g4 ! Delay-slot
andcc %g5, %g4, %g0 ! Executable?
+
+/* ITLB ** ICACHE line 2: Real faults */
be,pn %xcc, 3f ! Nope, branch.
nop ! Delay-slot
2: stxa %g5, [%g0] ASI_ITLB_DATA_IN ! Load PTE into TLB
retry ! Trap return
-3: rdpr %pstate, %g4 ! Move into alternate globals
-
-/* ITLB ** ICACHE line 2: Real faults */
+3: rdpr %pstate, %g4 ! Move into alt-globals
wrpr %g4, PSTATE_AG|PSTATE_MG, %pstate
rdpr %tpc, %g5 ! And load faulting VA
mov FAULT_CODE_ITLB, %g4 ! It was read from ITLB
-sparc64_realfault_common: ! Called by TL0 dtlb_miss too
+
+/* ITLB ** ICACHE line 3: Finish faults */
+sparc64_realfault_common: ! Called by dtlb_miss
stb %g4, [%g6 + TI_FAULT_CODE]
stx %g5, [%g6 + TI_FAULT_ADDR]
ba,pt %xcc, etrap ! Save state
1: rd %pc, %g7 ! ...
- nop
-
-/* ITLB ** ICACHE line 3: Finish faults + window fixups */
call do_sparc64_fault ! Call fault handler
add %sp, PTREGS_OFF, %o0! Compute pt_regs arg
ba,pt %xcc, rtrap_clr_l6 ! Restore cpu state
nop
+
+/* ITLB ** ICACHE line 4: Window fixups */
winfix_trampoline:
rdpr %tpc, %g3 ! Prepare winfixup TNPC
- or %g3, 0x7c, %g3 ! Compute offset to branch
+ or %g3, 0x7c, %g3 ! Compute branch offset
wrpr %g3, %tnpc ! Write it into TNPC
done ! Do it to it
-
-/* ITLB ** ICACHE line 4: Unused... */
nop
nop
nop
nop
- CREATE_VPTE_NOP
#undef CREATE_VPTE_OFFSET1
#undef CREATE_VPTE_OFFSET2
-#undef CREATE_VPTE_NOP
diff --git a/arch/sparc64/kernel/ktlb.S b/arch/sparc64/kernel/ktlb.S
index 7796b37f478..d9244d3c9f7 100644
--- a/arch/sparc64/kernel/ktlb.S
+++ b/arch/sparc64/kernel/ktlb.S
@@ -58,9 +58,6 @@ vpte_noent:
done
vpte_insn_obp:
- sethi %hi(prom_pmd_phys), %g5
- ldx [%g5 + %lo(prom_pmd_phys)], %g5
-
/* Behave as if we are at TL0. */
wrpr %g0, 1, %tl
rdpr %tpc, %g4 /* Find original faulting iaddr */
@@ -71,58 +68,57 @@ vpte_insn_obp:
mov TLB_SFSR, %g1
stxa %g4, [%g1 + %g1] ASI_IMMU
- /* Get PMD offset. */
- srlx %g4, 23, %g6
- and %g6, 0x7ff, %g6
- sllx %g6, 2, %g6
-
- /* Load PMD, is it valid? */
- lduwa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brz,pn %g5, longpath
- sllx %g5, 11, %g5
-
- /* Get PTE offset. */
- srlx %g4, 13, %g6
- and %g6, 0x3ff, %g6
- sllx %g6, 3, %g6
-
- /* Load PTE. */
- ldxa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brgez,pn %g5, longpath
- nop
-
- /* TLB load and return from trap. */
+ sethi %hi(prom_trans), %g5
+ or %g5, %lo(prom_trans), %g5
+
+1: ldx [%g5 + 0x00], %g6 ! base
+ brz,a,pn %g6, longpath ! no more entries, fail
+ mov TLB_SFSR, %g1 ! and restore %g1
+ ldx [%g5 + 0x08], %g1 ! len
+ add %g6, %g1, %g1 ! end
+ cmp %g6, %g4
+ bgu,pt %xcc, 2f
+ cmp %g4, %g1
+ bgeu,pt %xcc, 2f
+ ldx [%g5 + 0x10], %g1 ! PTE
+
+ /* TLB load, restore %g1, and return from trap. */
+ sub %g4, %g6, %g6
+ add %g1, %g6, %g5
+ mov TLB_SFSR, %g1
stxa %g5, [%g0] ASI_ITLB_DATA_IN
retry
-kvmap_do_obp:
- sethi %hi(prom_pmd_phys), %g5
- ldx [%g5 + %lo(prom_pmd_phys)], %g5
-
- /* Get PMD offset. */
- srlx %g4, 23, %g6
- and %g6, 0x7ff, %g6
- sllx %g6, 2, %g6
-
- /* Load PMD, is it valid? */
- lduwa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brz,pn %g5, longpath
- sllx %g5, 11, %g5
-
- /* Get PTE offset. */
- srlx %g4, 13, %g6
- and %g6, 0x3ff, %g6
- sllx %g6, 3, %g6
-
- /* Load PTE. */
- ldxa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
- brgez,pn %g5, longpath
- nop
+2: ba,pt %xcc, 1b
+ add %g5, (3 * 8), %g5 ! next entry
- /* TLB load and return from trap. */
+kvmap_do_obp:
+ sethi %hi(prom_trans), %g5
+ or %g5, %lo(prom_trans), %g5
+ srlx %g4, 13, %g4
+ sllx %g4, 13, %g4
+
+1: ldx [%g5 + 0x00], %g6 ! base
+ brz,a,pn %g6, longpath ! no more entries, fail
+ mov TLB_SFSR, %g1 ! and restore %g1
+ ldx [%g5 + 0x08], %g1 ! len
+ add %g6, %g1, %g1 ! end
+ cmp %g6, %g4
+ bgu,pt %xcc, 2f
+ cmp %g4, %g1
+ bgeu,pt %xcc, 2f
+ ldx [%g5 + 0x10], %g1 ! PTE
+
+ /* TLB load, restore %g1, and return from trap. */
+ sub %g4, %g6, %g6
+ add %g1, %g6, %g5
+ mov TLB_SFSR, %g1
stxa %g5, [%g0] ASI_DTLB_DATA_IN
retry
+2: ba,pt %xcc, 1b
+ add %g5, (3 * 8), %g5 ! next entry
+
/*
* On a first level data miss, check whether this is to the OBP range (note
* that such accesses can be made by prom, as well as by kernel using
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index 0d2e967c720..1e44ee26cee 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -105,7 +105,7 @@ static void __init read_obp_memory(const char *property,
regs[i].phys_addr = base;
regs[i].reg_size = size;
}
- sort(regs, ents, sizeof(struct linux_prom64_registers),
+ sort(regs, ents, sizeof(struct linux_prom64_registers),
cmp_p64, NULL);
}
@@ -367,8 +367,11 @@ struct linux_prom_translation {
unsigned long size;
unsigned long data;
};
-static struct linux_prom_translation prom_trans[512] __initdata;
-static unsigned int prom_trans_ents __initdata;
+
+/* Exported for kernel TLB miss handling in ktlb.S */
+struct linux_prom_translation prom_trans[512] __read_mostly;
+unsigned int prom_trans_ents __read_mostly;
+unsigned int swapper_pgd_zero __read_mostly;
extern unsigned long prom_boot_page;
extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle);
@@ -378,122 +381,57 @@ extern void register_prom_callbacks(void);
/* Exported for SMP bootup purposes. */
unsigned long kern_locked_tte_data;
-/* Exported for kernel TLB miss handling in ktlb.S */
-unsigned long prom_pmd_phys __read_mostly;
-unsigned int swapper_pgd_zero __read_mostly;
-
-static pmd_t *prompmd __read_mostly;
-
-#define BASE_PAGE_SIZE 8192
-
/*
* Translate PROM's mapping we capture at boot time into physical address.
* The second parameter is only set from prom_callback() invocations.
*/
unsigned long prom_virt_to_phys(unsigned long promva, int *error)
{
- pmd_t *pmdp = prompmd + ((promva >> 23) & 0x7ff);
- pte_t *ptep;
- unsigned long base;
-
- if (pmd_none(*pmdp)) {
- if (error)
- *error = 1;
- return 0;
- }
- ptep = (pte_t *)__pmd_page(*pmdp) + ((promva >> 13) & 0x3ff);
- if (!pte_present(*ptep)) {
- if (error)
- *error = 1;
- return 0;
- }
- if (error) {
- *error = 0;
- return pte_val(*ptep);
- }
- base = pte_val(*ptep) & _PAGE_PADDR;
-
- return base + (promva & (BASE_PAGE_SIZE - 1));
-}
+ int i;
-/* The obp translations are saved based on 8k pagesize, since obp can
- * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
- * HI_OBP_ADDRESS range are handled in entry.S and do not use the vpte
- * scheme (also, see rant in inherit_locked_prom_mappings()).
- */
-static void __init build_obp_range(unsigned long start, unsigned long end, unsigned long data)
-{
- unsigned long vaddr;
+ for (i = 0; i < prom_trans_ents; i++) {
+ struct linux_prom_translation *p = &prom_trans[i];
- for (vaddr = start; vaddr < end; vaddr += BASE_PAGE_SIZE) {
- unsigned long val;
- pmd_t *pmd;
- pte_t *pte;
+ if (promva >= p->virt &&
+ promva < (p->virt + p->size)) {
+ unsigned long base = p->data & _PAGE_PADDR;
- pmd = prompmd + ((vaddr >> 23) & 0x7ff);
- if (pmd_none(*pmd)) {
- pte = __alloc_bootmem(BASE_PAGE_SIZE, BASE_PAGE_SIZE,
- PAGE_SIZE);
- if (!pte)
- prom_halt();
- memset(pte, 0, BASE_PAGE_SIZE);
- pmd_set(pmd, pte);
+ if (error)
+ *error = 0;
+ return base + (promva & (8192 - 1));
}
- pte = (pte_t *) __pmd_page(*pmd) + ((vaddr >> 13) & 0x3ff);
-
- val = data;
-
- /* Clear diag TTE bits. */
- if (tlb_type == spitfire)
- val &= ~0x0003fe0000000000UL;
-
- set_pte_at(&init_mm, vaddr, pte,
- __pte(val | _PAGE_MODIFIED));
-
- data += BASE_PAGE_SIZE;
}
+ if (error)
+ *error = 1;
+ return 0UL;
}
+/* The obp translations are saved based on 8k pagesize, since obp can
+ * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
+ * HI_OBP_ADDRESS range are handled in ktlb.S and do not use the vpte
+ * scheme (also, see rant in inherit_locked_prom_mappings()).
+ */
static inline int in_obp_range(unsigned long vaddr)
{
return (vaddr >= LOW_OBP_ADDRESS &&
vaddr < HI_OBP_ADDRESS);
}
-#define OBP_PMD_SIZE 2048
-static void __init build_obp_pgtable(void)
+static int cmp_ptrans(const void *a, const void *b)
{
- unsigned long i;
-
- prompmd = __alloc_bootmem(OBP_PMD_SIZE, OBP_PMD_SIZE, PAGE_SIZE);
- if (!prompmd)
- prom_halt();
-
- memset(prompmd, 0, OBP_PMD_SIZE);
-
- prom_pmd_phys = __pa(prompmd);
-
- for (i = 0; i < prom_trans_ents; i++) {
- unsigned long start, end;
-
- if (!in_obp_range(prom_trans[i].virt))
- continue;
+ const struct linux_prom_translation *x = a, *y = b;
- start = prom_trans[i].virt;
- end = start + prom_trans[i].size;
- if (end > HI_OBP_ADDRESS)
- end = HI_OBP_ADDRESS;
-
- build_obp_range(start, end, prom_trans[i].data);
- }
+ if (x->virt > y->virt)
+ return 1;
+ if (x->virt < y->virt)
+ return -1;
+ return 0;
}
-/* Read OBP translations property into 'prom_trans[]'.
- * Return the number of entries.
- */
+/* Read OBP translations property into 'prom_trans[]'. */
static void __init read_obp_translations(void)
{
- int n, node;
+ int n, node, ents, first, last, i;
node = prom_finddevice("/virtual-memory");
n = prom_getproplen(node, "translations");
@@ -515,7 +453,41 @@ static void __init read_obp_translations(void)
n = n / sizeof(struct linux_prom_translation);
- prom_trans_ents = n;
+ ents = n;
+
+ sort(prom_trans, ents, sizeof(struct linux_prom_translation),
+ cmp_ptrans, NULL);
+
+ /* Now kick out all the non-OBP entries. */
+ for (i = 0; i < ents; i++) {
+ if (in_obp_range(prom_trans[i].virt))
+ break;
+ }
+ first = i;
+ for (; i < ents; i++) {
+ if (!in_obp_range(prom_trans[i].virt))
+ break;
+ }
+ last = i;
+
+ for (i = 0; i < (last - first); i++) {
+ struct linux_prom_translation *src = &prom_trans[i + first];
+ struct linux_prom_translation *dest = &prom_trans[i];
+
+ *dest = *src;
+ }
+ for (; i < ents; i++) {
+ struct linux_prom_translation *dest = &prom_trans[i];
+ dest->virt = dest->size = dest->data = 0x0UL;
+ }
+
+ prom_trans_ents = last - first;
+
+ if (tlb_type == spitfire) {
+ /* Clear diag TTE bits. */
+ for (i = 0; i < prom_trans_ents; i++)
+ prom_trans[i].data &= ~0x0003fe0000000000UL;
+ }
}
static void __init remap_kernel(void)
@@ -553,21 +525,18 @@ static void __init remap_kernel(void)
}
-static void __init inherit_prom_mappings_pre(void)
+static void __init inherit_prom_mappings(void)
{
read_obp_translations();
/* Now fixup OBP's idea about where we really are mapped. */
prom_printf("Remapping the kernel... ");
remap_kernel();
-
prom_printf("done.\n");
-}
-static void __init inherit_prom_mappings_post(void)
-{
- build_obp_pgtable();
+ prom_printf("Registering callbacks... ");
register_prom_callbacks();
+ prom_printf("done.\n");
}
/* The OBP specifications for sun4u mark 0xfffffffc00000000 and
@@ -1519,7 +1488,7 @@ void __init paging_init(void)
swapper_pgd_zero = pgd_val(swapper_pg_dir[0]);
- inherit_prom_mappings_pre();
+ inherit_prom_mappings();
/* Ok, we can use our TLB miss and window trap handlers safely.
* We need to do a quick peek here to see if we are on StarFire
@@ -1530,23 +1499,15 @@ void __init paging_init(void)
extern void setup_tba(int);
setup_tba(this_is_starfire);
}
- __flush_tlb_all();
- /* Everything from this point forward, until we are done with
- * inherit_prom_mappings_post(), must complete successfully
- * without calling into the firmware. The firwmare page tables
- * have not been built, but we are running on the Linux kernel's
- * trap table.
- */
+ inherit_locked_prom_mappings(1);
+
+ __flush_tlb_all();
/* Setup bootmem... */
pages_avail = 0;
last_valid_pfn = end_pfn = bootmem_init(&pages_avail);
- inherit_prom_mappings_post();
-
- inherit_locked_prom_mappings(1);
-
#ifdef CONFIG_DEBUG_PAGEALLOC
kernel_physical_mapping_init();
#endif
diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile
index 783e18cae09..de17d4c6e02 100644
--- a/arch/um/drivers/Makefile
+++ b/arch/um/drivers/Makefile
@@ -13,7 +13,7 @@ mcast-objs := mcast_kern.o mcast_user.o
net-objs := net_kern.o net_user.o
mconsole-objs := mconsole_kern.o mconsole_user.o
hostaudio-objs := hostaudio_kern.o
-ubd-objs := ubd_kern.o
+ubd-objs := ubd_kern.o ubd_user.o
port-objs := port_kern.o port_user.o
harddog-objs := harddog_kern.o harddog_user.o
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
index e77a38da435..f73134333f6 100644
--- a/arch/um/drivers/ubd_kern.c
+++ b/arch/um/drivers/ubd_kern.c
@@ -35,7 +35,6 @@
#include "linux/blkpg.h"
#include "linux/genhd.h"
#include "linux/spinlock.h"
-#include "asm/atomic.h"
#include "asm/segment.h"
#include "asm/uaccess.h"
#include "asm/irq.h"
@@ -54,21 +53,20 @@
#include "mem.h"
#include "mem_kern.h"
#include "cow.h"
-#include "aio.h"
enum ubd_req { UBD_READ, UBD_WRITE };
struct io_thread_req {
- enum aio_type op;
+ enum ubd_req op;
int fds[2];
unsigned long offsets[2];
unsigned long long offset;
unsigned long length;
char *buffer;
int sectorsize;
- int bitmap_offset;
- long bitmap_start;
- long bitmap_end;
+ unsigned long sector_mask;
+ unsigned long long cow_offset;
+ unsigned long bitmap_words[2];
int error;
};
@@ -82,31 +80,28 @@ extern int create_cow_file(char *cow_file, char *backing_file,
unsigned long *bitmap_len_out,
int *data_offset_out);
extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
-extern void do_io(struct io_thread_req *req, struct request *r,
- unsigned long *bitmap);
+extern void do_io(struct io_thread_req *req);
-static inline int ubd_test_bit(__u64 bit, void *data)
+static inline int ubd_test_bit(__u64 bit, unsigned char *data)
{
- unsigned char *buffer = data;
__u64 n;
int bits, off;
- bits = sizeof(buffer[0]) * 8;
+ bits = sizeof(data[0]) * 8;
n = bit / bits;
off = bit % bits;
- return((buffer[n] & (1 << off)) != 0);
+ return((data[n] & (1 << off)) != 0);
}
-static inline void ubd_set_bit(__u64 bit, void *data)
+static inline void ubd_set_bit(__u64 bit, unsigned char *data)
{
- unsigned char *buffer = data;
__u64 n;
int bits, off;
- bits = sizeof(buffer[0]) * 8;
+ bits = sizeof(data[0]) * 8;
n = bit / bits;
off = bit % bits;
- buffer[n] |= (1 << off);
+ data[n] |= (1 << off);
}
/*End stuff from ubd_user.h*/
@@ -115,6 +110,8 @@ static inline void ubd_set_bit(__u64 bit, void *data)
static DEFINE_SPINLOCK(ubd_io_lock);
static DEFINE_SPINLOCK(ubd_lock);
+static void (*do_ubd)(void);
+
static int ubd_open(struct inode * inode, struct file * filp);
static int ubd_release(struct inode * inode, struct file * file);
static int ubd_ioctl(struct inode * inode, struct file * file,
@@ -161,8 +158,6 @@ struct cow {
int data_offset;
};
-#define MAX_SG 64
-
struct ubd {
char *file;
int count;
@@ -173,7 +168,6 @@ struct ubd {
int no_cow;
struct cow cow;
struct platform_device pdev;
- struct scatterlist sg[MAX_SG];
};
#define DEFAULT_COW { \
@@ -466,114 +460,81 @@ __uml_help(fakehd,
);
static void do_ubd_request(request_queue_t * q);
-static int in_ubd;
+
+/* Only changed by ubd_init, which is an initcall. */
+int thread_fd = -1;
/* Changed by ubd_handler, which is serialized because interrupts only
* happen on CPU 0.
*/
int intr_count = 0;
-static void ubd_end_request(struct request *req, int bytes, int uptodate)
+/* call ubd_finish if you need to serialize */
+static void __ubd_finish(struct request *req, int error)
{
- if (!end_that_request_first(req, uptodate, bytes >> 9)) {
- add_disk_randomness(req->rq_disk);
- end_that_request_last(req);
+ int nsect;
+
+ if(error){
+ end_request(req, 0);
+ return;
}
+ nsect = req->current_nr_sectors;
+ req->sector += nsect;
+ req->buffer += nsect << 9;
+ req->errors = 0;
+ req->nr_sectors -= nsect;
+ req->current_nr_sectors = 0;
+ end_request(req, 1);
}
-/* call ubd_finish if you need to serialize */
-static void __ubd_finish(struct request *req, int bytes)
+static inline void ubd_finish(struct request *req, int error)
{
- if(bytes < 0){
- ubd_end_request(req, 0, 0);
- return;
- }
-
- ubd_end_request(req, bytes, 1);
+ spin_lock(&ubd_io_lock);
+ __ubd_finish(req, error);
+ spin_unlock(&ubd_io_lock);
}
-static inline void ubd_finish(struct request *req, int bytes)
+/* Called without ubd_io_lock held */
+static void ubd_handler(void)
{
- spin_lock(&ubd_io_lock);
- __ubd_finish(req, bytes);
- spin_unlock(&ubd_io_lock);
+ struct io_thread_req req;
+ struct request *rq = elv_next_request(ubd_queue);
+ int n;
+
+ do_ubd = NULL;
+ intr_count++;
+ n = os_read_file(thread_fd, &req, sizeof(req));
+ if(n != sizeof(req)){
+ printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, "
+ "err = %d\n", os_getpid(), -n);
+ spin_lock(&ubd_io_lock);
+ end_request(rq, 0);
+ spin_unlock(&ubd_io_lock);
+ return;
+ }
+
+ ubd_finish(rq, req.error);
+ reactivate_fd(thread_fd, UBD_IRQ);
+ do_ubd_request(ubd_queue);
}
-struct bitmap_io {
- atomic_t count;
- struct aio_context aio;
-};
-
-struct ubd_aio {
- struct aio_context aio;
- struct request *req;
- int len;
- struct bitmap_io *bitmap;
- void *bitmap_buf;
-};
-
-static int ubd_reply_fd = -1;
-
static irqreturn_t ubd_intr(int irq, void *dev, struct pt_regs *unused)
{
- struct aio_thread_reply reply;
- struct ubd_aio *aio;
- struct request *req;
- int err, n, fd = (int) (long) dev;
-
- while(1){
- err = os_read_file(fd, &reply, sizeof(reply));
- if(err == -EAGAIN)
- break;
- if(err < 0){
- printk("ubd_aio_handler - read returned err %d\n",
- -err);
- break;
- }
-
- aio = container_of(reply.data, struct ubd_aio, aio);
- n = reply.err;
-
- if(n == 0){
- req = aio->req;
- req->nr_sectors -= aio->len >> 9;
-
- if((aio->bitmap != NULL) &&
- (atomic_dec_and_test(&aio->bitmap->count))){
- aio->aio = aio->bitmap->aio;
- aio->len = 0;
- kfree(aio->bitmap);
- aio->bitmap = NULL;
- submit_aio(&aio->aio);
- }
- else {
- if((req->nr_sectors == 0) &&
- (aio->bitmap == NULL)){
- int len = req->hard_nr_sectors << 9;
- ubd_finish(req, len);
- }
-
- if(aio->bitmap_buf != NULL)
- kfree(aio->bitmap_buf);
- kfree(aio);
- }
- }
- else if(n < 0){
- ubd_finish(aio->req, n);
- if(aio->bitmap != NULL)
- kfree(aio->bitmap);
- if(aio->bitmap_buf != NULL)
- kfree(aio->bitmap_buf);
- kfree(aio);
- }
- }
- reactivate_fd(fd, UBD_IRQ);
+ ubd_handler();
+ return(IRQ_HANDLED);
+}
- do_ubd_request(ubd_queue);
+/* Only changed by ubd_init, which is an initcall. */
+static int io_pid = -1;
- return(IRQ_HANDLED);
+void kill_io_thread(void)
+{
+ if(io_pid != -1)
+ os_kill_process(io_pid, 1);
}
+__uml_exitcall(kill_io_thread);
+
static int ubd_file_size(struct ubd *dev, __u64 *size_out)
{
char *file;
@@ -608,7 +569,7 @@ static int ubd_open_dev(struct ubd *dev)
&dev->cow.data_offset, create_ptr);
if((dev->fd == -ENOENT) && create_cow){
- dev->fd = create_cow_file(dev->file, dev->cow.file,
+ dev->fd = create_cow_file(dev->file, dev->cow.file,
dev->openflags, 1 << 9, PAGE_SIZE,
&dev->cow.bitmap_offset,
&dev->cow.bitmap_len,
@@ -870,10 +831,6 @@ int ubd_init(void)
{
int i;
- ubd_reply_fd = init_aio_irq(UBD_IRQ, "ubd", ubd_intr);
- if(ubd_reply_fd < 0)
- printk("Setting up ubd AIO failed, err = %d\n", ubd_reply_fd);
-
devfs_mk_dir("ubd");
if (register_blkdev(MAJOR_NR, "ubd"))
return -1;
@@ -884,7 +841,6 @@ int ubd_init(void)
return -1;
}
- blk_queue_max_hw_segments(ubd_queue, MAX_SG);
if (fake_major != MAJOR_NR) {
char name[sizeof("ubd_nnn\0")];
@@ -896,12 +852,40 @@ int ubd_init(void)
driver_register(&ubd_driver);
for (i = 0; i < MAX_DEV; i++)
ubd_add(i);
-
return 0;
}
late_initcall(ubd_init);
+int ubd_driver_init(void){
+ unsigned long stack;
+ int err;
+
+ /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
+ if(global_openflags.s){
+ printk(KERN_INFO "ubd: Synchronous mode\n");
+ /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
+ * enough. So use anyway the io thread. */
+ }
+ stack = alloc_stack(0, 0);
+ io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
+ &thread_fd);
+ if(io_pid < 0){
+ printk(KERN_ERR
+ "ubd : Failed to start I/O thread (errno = %d) - "
+ "falling back to synchronous I/O\n", -io_pid);
+ io_pid = -1;
+ return(0);
+ }
+ err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
+ SA_INTERRUPT, "ubd", ubd_dev);
+ if(err != 0)
+ printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
+ return(err);
+}
+
+device_initcall(ubd_driver_init);
+
static int ubd_open(struct inode *inode, struct file *filp)
{
struct gendisk *disk = inode->i_bdev->bd_disk;
@@ -939,55 +923,105 @@ static int ubd_release(struct inode * inode, struct file * file)
return(0);
}
-static void cowify_bitmap(struct io_thread_req *req, unsigned long *bitmap)
+static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
+ __u64 *cow_offset, unsigned long *bitmap,
+ __u64 bitmap_offset, unsigned long *bitmap_words,
+ __u64 bitmap_len)
{
- __u64 sector = req->offset / req->sectorsize;
- int i;
+ __u64 sector = io_offset >> 9;
+ int i, update_bitmap = 0;
+
+ for(i = 0; i < length >> 9; i++){
+ if(cow_mask != NULL)
+ ubd_set_bit(i, (unsigned char *) cow_mask);
+ if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
+ continue;
- for(i = 0; i < req->length / req->sectorsize; i++){
- if(ubd_test_bit(sector + i, bitmap))
- continue;
+ update_bitmap = 1;
+ ubd_set_bit(sector + i, (unsigned char *) bitmap);
+ }
+
+ if(!update_bitmap)
+ return;
- if(req->bitmap_start == -1)
- req->bitmap_start = sector + i;
- req->bitmap_end = sector + i + 1;
+ *cow_offset = sector / (sizeof(unsigned long) * 8);
- ubd_set_bit(sector + i, bitmap);
- }
+ /* This takes care of the case where we're exactly at the end of the
+ * device, and *cow_offset + 1 is off the end. So, just back it up
+ * by one word. Thanks to Lynn Kerby for the fix and James McMechan
+ * for the original diagnosis.
+ */
+ if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
+ sizeof(unsigned long) - 1))
+ (*cow_offset)--;
+
+ bitmap_words[0] = bitmap[*cow_offset];
+ bitmap_words[1] = bitmap[*cow_offset + 1];
+
+ *cow_offset *= sizeof(unsigned long);
+ *cow_offset += bitmap_offset;
+}
+
+static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
+ __u64 bitmap_offset, __u64 bitmap_len)
+{
+ __u64 sector = req->offset >> 9;
+ int i;
+
+ if(req->length > (sizeof(req->sector_mask) * 8) << 9)
+ panic("Operation too long");
+
+ if(req->op == UBD_READ) {
+ for(i = 0; i < req->length >> 9; i++){
+ if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
+ ubd_set_bit(i, (unsigned char *)
+ &req->sector_mask);
+ }
+ }
+ else cowify_bitmap(req->offset, req->length, &req->sector_mask,
+ &req->cow_offset, bitmap, bitmap_offset,
+ req->bitmap_words, bitmap_len);
}
/* Called with ubd_io_lock held */
-static int prepare_request(struct request *req, struct io_thread_req *io_req,
- unsigned long long offset, int page_offset,
- int len, struct page *page)
+static int prepare_request(struct request *req, struct io_thread_req *io_req)
{
struct gendisk *disk = req->rq_disk;
struct ubd *dev = disk->private_data;
+ __u64 offset;
+ int len;
+
+ if(req->rq_status == RQ_INACTIVE) return(1);
/* This should be impossible now */
if((rq_data_dir(req) == WRITE) && !dev->openflags.w){
printk("Write attempted on readonly ubd device %s\n",
disk->disk_name);
- ubd_end_request(req, 0, 0);
+ end_request(req, 0);
return(1);
}
+ offset = ((__u64) req->sector) << 9;
+ len = req->current_nr_sectors << 9;
+
io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd;
io_req->fds[1] = dev->fd;
+ io_req->cow_offset = -1;
io_req->offset = offset;
io_req->length = len;
io_req->error = 0;
- io_req->op = (rq_data_dir(req) == READ) ? AIO_READ : AIO_WRITE;
+ io_req->sector_mask = 0;
+
+ io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
io_req->offsets[0] = 0;
io_req->offsets[1] = dev->cow.data_offset;
- io_req->buffer = page_address(page) + page_offset;
+ io_req->buffer = req->buffer;
io_req->sectorsize = 1 << 9;
- io_req->bitmap_offset = dev->cow.bitmap_offset;
- io_req->bitmap_start = -1;
- io_req->bitmap_end = -1;
- if((dev->cow.file != NULL) && (io_req->op == UBD_WRITE))
- cowify_bitmap(io_req, dev->cow.bitmap);
+ if(dev->cow.file != NULL)
+ cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset,
+ dev->cow.bitmap_len);
+
return(0);
}
@@ -996,36 +1030,30 @@ static void do_ubd_request(request_queue_t *q)
{
struct io_thread_req io_req;
struct request *req;
- __u64 sector;
- int err;
-
- if(in_ubd)
- return;
- in_ubd = 1;
- while((req = elv_next_request(q)) != NULL){
- struct gendisk *disk = req->rq_disk;
- struct ubd *dev = disk->private_data;
- int n, i;
-
- blkdev_dequeue_request(req);
-
- sector = req->sector;
- n = blk_rq_map_sg(q, req, dev->sg);
-
- for(i = 0; i < n; i++){
- struct scatterlist *sg = &dev->sg[i];
-
- err = prepare_request(req, &io_req, sector << 9,
- sg->offset, sg->length,
- sg->page);
- if(err)
- continue;
-
- sector += sg->length >> 9;
- do_io(&io_req, req, dev->cow.bitmap);
+ int err, n;
+
+ if(thread_fd == -1){
+ while((req = elv_next_request(q)) != NULL){
+ err = prepare_request(req, &io_req);
+ if(!err){
+ do_io(&io_req);
+ __ubd_finish(req, io_req.error);
+ }
+ }
+ }
+ else {
+ if(do_ubd || (req = elv_next_request(q)) == NULL)
+ return;
+ err = prepare_request(req, &io_req);
+ if(!err){
+ do_ubd = ubd_handler;
+ n = os_write_file(thread_fd, (char *) &io_req,
+ sizeof(io_req));
+ if(n != sizeof(io_req))
+ printk("write to io thread failed, "
+ "errno = %d\n", -n);
}
}
- in_ubd = 0;
}
static int ubd_ioctl(struct inode * inode, struct file * file,
@@ -1241,95 +1269,131 @@ int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
return(err);
}
-void do_io(struct io_thread_req *req, struct request *r, unsigned long *bitmap)
+static int update_bitmap(struct io_thread_req *req)
{
- struct ubd_aio *aio;
- struct bitmap_io *bitmap_io = NULL;
- char *buf;
- void *bitmap_buf = NULL;
- unsigned long len, sector;
- int nsectors, start, end, bit, err;
- __u64 off;
-
- if(req->bitmap_start != -1){
- /* Round up to the nearest word */
- int round = sizeof(unsigned long);
- len = (req->bitmap_end - req->bitmap_start +
- round * 8 - 1) / (round * 8);
- len *= round;
-
- off = req->bitmap_start / (8 * round);
- off *= round;
-
- bitmap_io = kmalloc(sizeof(*bitmap_io), GFP_KERNEL);
- if(bitmap_io == NULL){
- printk("Failed to kmalloc bitmap IO\n");
- req->error = 1;
- return;
- }
+ int n;
- bitmap_buf = kmalloc(len, GFP_KERNEL);
- if(bitmap_buf == NULL){
- printk("do_io : kmalloc of bitmap chunk "
- "failed\n");
- kfree(bitmap_io);
- req->error = 1;
- return;
- }
- memcpy(bitmap_buf, &bitmap[off / sizeof(bitmap[0])], len);
-
- *bitmap_io = ((struct bitmap_io)
- { .count = ATOMIC_INIT(0),
- .aio = INIT_AIO(AIO_WRITE, req->fds[1],
- bitmap_buf, len,
- req->bitmap_offset + off,
- ubd_reply_fd) } );
- }
+ if(req->cow_offset == -1)
+ return(0);
- nsectors = req->length / req->sectorsize;
- start = 0;
- end = nsectors;
- bit = 0;
- do {
- if(bitmap != NULL){
- sector = req->offset / req->sectorsize;
- bit = ubd_test_bit(sector + start, bitmap);
- end = start;
- while((end < nsectors) &&
- (ubd_test_bit(sector + end, bitmap) == bit))
- end++;
- }
+ n = os_seek_file(req->fds[1], req->cow_offset);
+ if(n < 0){
+ printk("do_io - bitmap lseek failed : err = %d\n", -n);
+ return(1);
+ }
- off = req->offsets[bit] + req->offset +
- start * req->sectorsize;
- len = (end - start) * req->sectorsize;
- buf = &req->buffer[start * req->sectorsize];
+ n = os_write_file(req->fds[1], &req->bitmap_words,
+ sizeof(req->bitmap_words));
+ if(n != sizeof(req->bitmap_words)){
+ printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
+ req->fds[1]);
+ return(1);
+ }
- aio = kmalloc(sizeof(*aio), GFP_KERNEL);
- if(aio == NULL){
- req->error = 1;
- return;
- }
+ return(0);
+}
- *aio = ((struct ubd_aio)
- { .aio = INIT_AIO(req->op, req->fds[bit], buf,
- len, off, ubd_reply_fd),
- .len = len,
- .req = r,
- .bitmap = bitmap_io,
- .bitmap_buf = bitmap_buf });
-
- if(aio->bitmap != NULL)
- atomic_inc(&aio->bitmap->count);
-
- err = submit_aio(&aio->aio);
- if(err){
- printk("do_io - submit_aio failed, "
- "err = %d\n", err);
- req->error = 1;
- return;
- }
+void do_io(struct io_thread_req *req)
+{
+ char *buf;
+ unsigned long len;
+ int n, nsectors, start, end, bit;
+ int err;
+ __u64 off;
+
+ nsectors = req->length / req->sectorsize;
+ start = 0;
+ do {
+ bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
+ end = start;
+ while((end < nsectors) &&
+ (ubd_test_bit(end, (unsigned char *)
+ &req->sector_mask) == bit))
+ end++;
+
+ off = req->offset + req->offsets[bit] +
+ start * req->sectorsize;
+ len = (end - start) * req->sectorsize;
+ buf = &req->buffer[start * req->sectorsize];
+
+ err = os_seek_file(req->fds[bit], off);
+ if(err < 0){
+ printk("do_io - lseek failed : err = %d\n", -err);
+ req->error = 1;
+ return;
+ }
+ if(req->op == UBD_READ){
+ n = 0;
+ do {
+ buf = &buf[n];
+ len -= n;
+ n = os_read_file(req->fds[bit], buf, len);
+ if (n < 0) {
+ printk("do_io - read failed, err = %d "
+ "fd = %d\n", -n, req->fds[bit]);
+ req->error = 1;
+ return;
+ }
+ } while((n < len) && (n != 0));
+ if (n < len) memset(&buf[n], 0, len - n);
+ } else {
+ n = os_write_file(req->fds[bit], buf, len);
+ if(n != len){
+ printk("do_io - write failed err = %d "
+ "fd = %d\n", -n, req->fds[bit]);
+ req->error = 1;
+ return;
+ }
+ }
+
+ start = end;
+ } while(start < nsectors);
- start = end;
- } while(start < nsectors);
+ req->error = update_bitmap(req);
}
+
+/* Changed in start_io_thread, which is serialized by being called only
+ * from ubd_init, which is an initcall.
+ */
+int kernel_fd = -1;
+
+/* Only changed by the io thread */
+int io_count = 0;
+
+int io_thread(void *arg)
+{
+ struct io_thread_req req;
+ int n;
+
+ ignore_sigwinch_sig();
+ while(1){
+ n = os_read_file(kernel_fd, &req, sizeof(req));
+ if(n != sizeof(req)){
+ if(n < 0)
+ printk("io_thread - read failed, fd = %d, "
+ "err = %d\n", kernel_fd, -n);
+ else {
+ printk("io_thread - short read, fd = %d, "
+ "length = %d\n", kernel_fd, n);
+ }
+ continue;
+ }
+ io_count++;
+ do_io(&req);
+ n = os_write_file(kernel_fd, &req, sizeof(req));
+ if(n != sizeof(req))
+ printk("io_thread - write failed, fd = %d, err = %d\n",
+ kernel_fd, -n);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c
new file mode 100644
index 00000000000..b94d2bc4fe0
--- /dev/null
+++ b/arch/um/drivers/ubd_user.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
+ * Licensed under the GPL
+ */
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include "asm/types.h"
+#include "user_util.h"
+#include "kern_util.h"
+#include "user.h"
+#include "ubd_user.h"
+#include "os.h"
+#include "cow.h"
+
+#include <endian.h>
+#include <byteswap.h>
+
+void ignore_sigwinch_sig(void)
+{
+ signal(SIGWINCH, SIG_IGN);
+}
+
+int start_io_thread(unsigned long sp, int *fd_out)
+{
+ int pid, fds[2], err;
+
+ err = os_pipe(fds, 1, 1);
+ if(err < 0){
+ printk("start_io_thread - os_pipe failed, err = %d\n", -err);
+ goto out;
+ }
+
+ kernel_fd = fds[0];
+ *fd_out = fds[1];
+
+ pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM | SIGCHLD,
+ NULL);
+ if(pid < 0){
+ printk("start_io_thread - clone failed : errno = %d\n", errno);
+ err = -errno;
+ goto out_close;
+ }
+
+ return(pid);
+
+ out_close:
+ os_close_file(fds[0]);
+ os_close_file(fds[1]);
+ kernel_fd = -1;
+ *fd_out = -1;
+ out:
+ return(err);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/arch/um/include/aio.h b/arch/um/include/aio.h
index 83f16877ab0..423bae9153f 100644
--- a/arch/um/include/aio.h
+++ b/arch/um/include/aio.h
@@ -14,27 +14,15 @@ struct aio_thread_reply {
};
struct aio_context {
- enum aio_type type;
- int fd;
- void *data;
- int len;
- unsigned long long offset;
int reply_fd;
struct aio_context *next;
};
-#define INIT_AIO(aio_type, aio_fd, aio_data, aio_len, aio_offset, \
- aio_reply_fd) \
- { .type = aio_type, \
- .fd = aio_fd, \
- .data = aio_data, \
- .len = aio_len, \
- .offset = aio_offset, \
- .reply_fd = aio_reply_fd }
-
#define INIT_AIO_CONTEXT { .reply_fd = -1, \
.next = NULL }
-extern int submit_aio(struct aio_context *aio);
+extern int submit_aio(enum aio_type type, int fd, char *buf, int len,
+ unsigned long long offset, int reply_fd,
+ struct aio_context *aio);
#endif
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 6f766e1faec..2e58e304b8b 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -6,6 +6,7 @@
#ifndef __OS_H__
#define __OS_H__
+#include "uml-config.h"
#include "asm/types.h"
#include "../os/include/file.h"
@@ -159,7 +160,11 @@ extern int can_do_skas(void);
/* Make sure they are clear when running in TT mode. Required by
* SEGV_MAYBE_FIXABLE */
+#ifdef UML_CONFIG_MODE_SKAS
#define clear_can_do_skas() do { ptrace_faultinfo = proc_mm = 0; } while (0)
+#else
+#define clear_can_do_skas() do {} while (0)
+#endif
/* mem.c */
extern int create_mem_file(unsigned long len);
diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c
index f6e64026f99..41cfb094420 100644
--- a/arch/um/os-Linux/aio.c
+++ b/arch/um/os-Linux/aio.c
@@ -6,7 +6,6 @@
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
-#include <string.h>
#include <errno.h>
#include <sched.h>
#include <sys/syscall.h>
@@ -17,31 +16,18 @@
#include "user.h"
#include "mode.h"
+struct aio_thread_req {
+ enum aio_type type;
+ int io_fd;
+ unsigned long long offset;
+ char *buf;
+ int len;
+ struct aio_context *aio;
+};
+
static int aio_req_fd_r = -1;
static int aio_req_fd_w = -1;
-static int update_aio(struct aio_context *aio, int res)
-{
- if(res < 0)
- aio->len = res;
- else if((res == 0) && (aio->type == AIO_READ)){
- /* This is the EOF case - we have hit the end of the file
- * and it ends in a partial block, so we fill the end of
- * the block with zeros and claim success.
- */
- memset(aio->data, 0, aio->len);
- aio->len = 0;
- }
- else if(res > 0){
- aio->len -= res;
- aio->data += res;
- aio->offset += res;
- return aio->len;
- }
-
- return 0;
-}
-
#if defined(HAVE_AIO_ABI)
#include <linux/aio_abi.h>
@@ -80,7 +66,8 @@ static long io_getevents(aio_context_t ctx_id, long min_nr, long nr,
* that it now backs the mmapped area.
*/
-static int do_aio(aio_context_t ctx, struct aio_context *aio)
+static int do_aio(aio_context_t ctx, enum aio_type type, int fd, char *buf,
+ int len, unsigned long long offset, struct aio_context *aio)
{
struct iocb iocb, *iocbp = &iocb;
char c;
@@ -88,39 +75,40 @@ static int do_aio(aio_context_t ctx, struct aio_context *aio)
iocb = ((struct iocb) { .aio_data = (unsigned long) aio,
.aio_reqprio = 0,
- .aio_fildes = aio->fd,
- .aio_buf = (unsigned long) aio->data,
- .aio_nbytes = aio->len,
- .aio_offset = aio->offset,
+ .aio_fildes = fd,
+ .aio_buf = (unsigned long) buf,
+ .aio_nbytes = len,
+ .aio_offset = offset,
.aio_reserved1 = 0,
.aio_reserved2 = 0,
.aio_reserved3 = 0 });
- switch(aio->type){
+ switch(type){
case AIO_READ:
iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ err = io_submit(ctx, 1, &iocbp);
break;
case AIO_WRITE:
iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
+ err = io_submit(ctx, 1, &iocbp);
break;
case AIO_MMAP:
iocb.aio_lio_opcode = IOCB_CMD_PREAD;
iocb.aio_buf = (unsigned long) &c;
iocb.aio_nbytes = sizeof(c);
+ err = io_submit(ctx, 1, &iocbp);
break;
default:
- printk("Bogus op in do_aio - %d\n", aio->type);
+ printk("Bogus op in do_aio - %d\n", type);
err = -EINVAL;
- goto out;
+ break;
}
- err = io_submit(ctx, 1, &iocbp);
if(err > 0)
err = 0;
else
err = -errno;
- out:
return err;
}
@@ -129,9 +117,8 @@ static aio_context_t ctx = 0;
static int aio_thread(void *arg)
{
struct aio_thread_reply reply;
- struct aio_context *aio;
struct io_event event;
- int err, n;
+ int err, n, reply_fd;
signal(SIGWINCH, SIG_IGN);
@@ -144,22 +131,14 @@ static int aio_thread(void *arg)
"errno = %d\n", errno);
}
else {
- /* This is safe as we've just a pointer here. */
- aio = (struct aio_context *) (long) event.data;
- if(update_aio(aio, event.res)){
- do_aio(ctx, aio);
- continue;
- }
-
reply = ((struct aio_thread_reply)
- { .data = aio,
- .err = aio->len });
- err = os_write_file(aio->reply_fd, &reply,
- sizeof(reply));
+ { .data = (void *) (long) event.data,
+ .err = event.res });
+ reply_fd = ((struct aio_context *) reply.data)->reply_fd;
+ err = os_write_file(reply_fd, &reply, sizeof(reply));
if(err != sizeof(reply))
- printk("aio_thread - write failed, "
- "fd = %d, err = %d\n", aio->reply_fd,
- -err);
+ printk("aio_thread - write failed, fd = %d, "
+ "err = %d\n", aio_req_fd_r, -err);
}
}
return 0;
@@ -167,35 +146,35 @@ static int aio_thread(void *arg)
#endif
-static int do_not_aio(struct aio_context *aio)
+static int do_not_aio(struct aio_thread_req *req)
{
char c;
int err;
- switch(aio->type){
+ switch(req->type){
case AIO_READ:
- err = os_seek_file(aio->fd, aio->offset);
+ err = os_seek_file(req->io_fd, req->offset);
if(err)
goto out;
- err = os_read_file(aio->fd, aio->data, aio->len);
+ err = os_read_file(req->io_fd, req->buf, req->len);
break;
case AIO_WRITE:
- err = os_seek_file(aio->fd, aio->offset);
+ err = os_seek_file(req->io_fd, req->offset);
if(err)
goto out;
- err = os_write_file(aio->fd, aio->data, aio->len);
+ err = os_write_file(req->io_fd, req->buf, req->len);
break;
case AIO_MMAP:
- err = os_seek_file(aio->fd, aio->offset);
+ err = os_seek_file(req->io_fd, req->offset);
if(err)
goto out;
- err = os_read_file(aio->fd, &c, sizeof(c));
+ err = os_read_file(req->io_fd, &c, sizeof(c));
break;
default:
- printk("do_not_aio - bad request type : %d\n", aio->type);
+ printk("do_not_aio - bad request type : %d\n", req->type);
err = -EINVAL;
break;
}
@@ -206,14 +185,14 @@ static int do_not_aio(struct aio_context *aio)
static int not_aio_thread(void *arg)
{
- struct aio_context *aio;
+ struct aio_thread_req req;
struct aio_thread_reply reply;
int err;
signal(SIGWINCH, SIG_IGN);
while(1){
- err = os_read_file(aio_req_fd_r, &aio, sizeof(aio));
- if(err != sizeof(aio)){
+ err = os_read_file(aio_req_fd_r, &req, sizeof(req));
+ if(err != sizeof(req)){
if(err < 0)
printk("not_aio_thread - read failed, "
"fd = %d, err = %d\n", aio_req_fd_r,
@@ -224,34 +203,17 @@ static int not_aio_thread(void *arg)
}
continue;
}
- again:
- err = do_not_aio(aio);
-
- if(update_aio(aio, err))
- goto again;
-
- reply = ((struct aio_thread_reply) { .data = aio,
- .err = aio->len });
- err = os_write_file(aio->reply_fd, &reply, sizeof(reply));
+ err = do_not_aio(&req);
+ reply = ((struct aio_thread_reply) { .data = req.aio,
+ .err = err });
+ err = os_write_file(req.aio->reply_fd, &reply, sizeof(reply));
if(err != sizeof(reply))
printk("not_aio_thread - write failed, fd = %d, "
"err = %d\n", aio_req_fd_r, -err);
}
}
-static int submit_aio_24(struct aio_context *aio)
-{
- int err;
-
- err = os_write_file(aio_req_fd_w, &aio, sizeof(aio));
- if(err == sizeof(aio))
- err = 0;
-
- return err;
-}
-
static int aio_pid = -1;
-static int (*submit_proc)(struct aio_context *aio);
static int init_aio_24(void)
{
@@ -283,33 +245,11 @@ static int init_aio_24(void)
#endif
printk("2.6 host AIO support not used - falling back to I/O "
"thread\n");
-
- submit_proc = submit_aio_24;
-
return 0;
}
#ifdef HAVE_AIO_ABI
#define DEFAULT_24_AIO 0
-static int submit_aio_26(struct aio_context *aio)
-{
- struct aio_thread_reply reply;
- int err;
-
- err = do_aio(ctx, aio);
- if(err){
- reply = ((struct aio_thread_reply) { .data = aio,
- .err = err });
- err = os_write_file(aio->reply_fd, &reply, sizeof(reply));
- if(err != sizeof(reply))
- printk("submit_aio_26 - write failed, "
- "fd = %d, err = %d\n", aio->reply_fd, -err);
- else err = 0;
- }
-
- return err;
-}
-
static int init_aio_26(void)
{
unsigned long stack;
@@ -330,22 +270,39 @@ static int init_aio_26(void)
aio_pid = err;
printk("Using 2.6 host AIO\n");
+ return 0;
+}
+
+static int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, struct aio_context *aio)
+{
+ struct aio_thread_reply reply;
+ int err;
- submit_proc = submit_aio_26;
+ err = do_aio(ctx, type, io_fd, buf, len, offset, aio);
+ if(err){
+ reply = ((struct aio_thread_reply) { .data = aio,
+ .err = err });
+ err = os_write_file(aio->reply_fd, &reply, sizeof(reply));
+ if(err != sizeof(reply))
+ printk("submit_aio_26 - write failed, "
+ "fd = %d, err = %d\n", aio->reply_fd, -err);
+ else err = 0;
+ }
- return 0;
+ return err;
}
#else
#define DEFAULT_24_AIO 1
-static int submit_aio_26(struct aio_context *aio)
+static int init_aio_26(void)
{
return -ENOSYS;
}
-static int init_aio_26(void)
+static int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, struct aio_context *aio)
{
- submit_proc = submit_aio_26;
return -ENOSYS;
}
#endif
@@ -412,7 +369,33 @@ static void exit_aio(void)
__uml_exitcall(exit_aio);
-int submit_aio(struct aio_context *aio)
+static int submit_aio_24(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, struct aio_context *aio)
{
- return (*submit_proc)(aio);
+ struct aio_thread_req req = { .type = type,
+ .io_fd = io_fd,
+ .offset = offset,
+ .buf = buf,
+ .len = len,
+ .aio = aio,
+ };
+ int err;
+
+ err = os_write_file(aio_req_fd_w, &req, sizeof(req));
+ if(err == sizeof(req))
+ err = 0;
+
+ return err;
+}
+
+int submit_aio(enum aio_type type, int io_fd, char *buf, int len,
+ unsigned long long offset, int reply_fd,
+ struct aio_context *aio)
+{
+ aio->reply_fd = reply_fd;
+ if(aio_24)
+ return submit_aio_24(type, io_fd, buf, len, offset, aio);
+ else {
+ return submit_aio_26(type, io_fd, buf, len, offset, aio);
+ }
}